diff --git a/Editor/MaterialWindow.cpp b/Editor/MaterialWindow.cpp index 94c7448f0..5ce7fdb6e 100644 --- a/Editor/MaterialWindow.cpp +++ b/Editor/MaterialWindow.cpp @@ -1,6 +1,8 @@ #include "stdafx.h" #include "MaterialWindow.h" +#include + using namespace wi::graphics; using namespace wi::ecs; using namespace wi::scene; @@ -130,6 +132,18 @@ void MaterialWindow::Create(EditorComponent* _editor) }); AddWidget(&outlineCheckBox); + preferUncompressedCheckBox.Create("Prefer Uncompressed Textures: "); + preferUncompressedCheckBox.SetTooltip("For uncompressed textures (jpg, png, etc.) or transcodable textures (KTX2, Basis) here it is possible to enable/disable auto block compression on importing. \nBlock compression can reduce GPU memory usage and improve performance, but it can result in degraded quality."); + preferUncompressedCheckBox.SetPos(XMFLOAT2(x, y += step)); + preferUncompressedCheckBox.SetSize(XMFLOAT2(hei, hei)); + preferUncompressedCheckBox.OnClick([&](wi::gui::EventArgs args) { + MaterialComponent* material = editor->GetCurrentScene().materials.GetComponent(entity); + if (material != nullptr) + material->SetPreferUncompressedTexturesEnabled(args.bValue); + textureSlotComboBox.SetSelected(textureSlotComboBox.GetSelected()); + }); + AddWidget(&preferUncompressedCheckBox); + shaderTypeComboBox.Create("Shader: "); shaderTypeComboBox.SetTooltip("Select a shader for this material. \nCustom shaders (*) will also show up here (see wi::renderer:RegisterCustomShader() for more info.)\nNote that custom shaders (*) can't select between blend modes, as they are created with an explicit blend mode."); @@ -577,63 +591,88 @@ void MaterialWindow::Create(EditorComponent* _editor) break; } } - textureSlotComboBox.OnSelect([this](wi::gui::EventArgs args) + textureSlotComboBox.OnSelect([this](wi::gui::EventArgs args) { + + std::string tooltiptext; + + switch (args.iValue) { + case MaterialComponent::BASECOLORMAP: + tooltiptext = "RGBA: Basecolor"; + break; + case MaterialComponent::NORMALMAP: + tooltiptext = "RG: Normal"; + break; + case MaterialComponent::SURFACEMAP: + tooltiptext = "Default workflow: R: Occlusion, G: Roughness, B: Metalness, A: Reflectance\nSpecular-glossiness workflow: RGB: Specular color (f0), A: smoothness"; + break; + case MaterialComponent::EMISSIVEMAP: + tooltiptext = "RGBA: Emissive"; + break; + case MaterialComponent::OCCLUSIONMAP: + tooltiptext = "R: Occlusion"; + break; + case MaterialComponent::DISPLACEMENTMAP: + tooltiptext = "R: Displacement heightmap"; + break; + case MaterialComponent::TRANSMISSIONMAP: + tooltiptext = "R: Transmission factor"; + break; + case MaterialComponent::SHEENCOLORMAP: + tooltiptext = "RGB: Sheen color"; + break; + case MaterialComponent::SHEENROUGHNESSMAP: + tooltiptext = "A: Roughness"; + break; + case MaterialComponent::CLEARCOATMAP: + tooltiptext = "R: Clearcoat factor"; + break; + case MaterialComponent::CLEARCOATROUGHNESSMAP: + tooltiptext = "G: Roughness"; + break; + case MaterialComponent::CLEARCOATNORMALMAP: + tooltiptext = "RG: Normal"; + break; + case MaterialComponent::SPECULARMAP: + tooltiptext = "RGB: Specular color, A: Specular intensity [non-metal]"; + break; + case MaterialComponent::ANISOTROPYMAP: + tooltiptext = "RG: The anisotropy texture. Red and green channels represent the anisotropy direction in [-1, 1] tangent, bitangent space.\nThe vector is rotated by anisotropyRotation, and multiplied by anisotropyStrength, to obtain the final anisotropy direction and strength."; + break; + default: + break; + } - switch (args.iValue) - { - case MaterialComponent::BASECOLORMAP: - textureSlotButton.SetTooltip("RGBA: Basecolor"); - break; - case MaterialComponent::NORMALMAP: - textureSlotButton.SetTooltip("RGB: Normal"); - break; - case MaterialComponent::SURFACEMAP: - textureSlotButton.SetTooltip("Default workflow: R: Occlusion, G: Roughness, B: Metalness, A: Reflectance\nSpecular-glossiness workflow: RGB: Specular color (f0), A: smoothness"); - break; - case MaterialComponent::EMISSIVEMAP: - textureSlotButton.SetTooltip("RGBA: Emissive"); - break; - case MaterialComponent::OCCLUSIONMAP: - textureSlotButton.SetTooltip("R: Occlusion"); - break; - case MaterialComponent::DISPLACEMENTMAP: - textureSlotButton.SetTooltip("R: Displacement heightmap"); - break; - case MaterialComponent::TRANSMISSIONMAP: - textureSlotButton.SetTooltip("R: Transmission factor"); - break; - case MaterialComponent::SHEENCOLORMAP: - textureSlotButton.SetTooltip("RGB: Sheen color"); - break; - case MaterialComponent::SHEENROUGHNESSMAP: - textureSlotButton.SetTooltip("A: Roughness"); - break; - case MaterialComponent::CLEARCOATMAP: - textureSlotButton.SetTooltip("R: Clearcoat factor"); - break; - case MaterialComponent::CLEARCOATROUGHNESSMAP: - textureSlotButton.SetTooltip("G: Roughness"); - break; - case MaterialComponent::CLEARCOATNORMALMAP: - textureSlotButton.SetTooltip("RGB: Normal"); - break; - case MaterialComponent::SPECULARMAP: - textureSlotButton.SetTooltip("RGB: Specular color, A: Specular intensity [non-metal]"); - break; - case MaterialComponent::ANISOTROPYMAP: - textureSlotButton.SetTooltip("RG: The anisotropy texture. Red and green channels represent the anisotropy direction in [-1, 1] tangent, bitangent space.\nThe vector is rotated by anisotropyRotation, and multiplied by anisotropyStrength, to obtain the final anisotropy direction and strength."); - break; - default: - break; - } - - MaterialComponent* material = editor->GetCurrentScene().materials.GetComponent(entity); - if (material == nullptr) - return; + MaterialComponent* material = editor->GetCurrentScene().materials.GetComponent(entity); + if (material != nullptr) + { textureSlotButton.SetImage(material->textures[args.iValue].resource); + if (material->textures[args.iValue].resource.IsValid()) + { + const Texture& texture = material->textures[args.iValue].resource.GetTexture(); + tooltiptext += "\nResolution: " + std::to_string(texture.desc.width) + " * " + std::to_string(texture.desc.height); + tooltiptext += "\nMip levels: " + std::to_string(texture.desc.mip_levels); + tooltiptext += "\nFormat: "; + tooltiptext += GetFormatString(texture.desc.format); - }); + std::stringstream ss; + ss << std::fixed << std::setprecision(2); + const size_t texture_size = ComputeTextureMemorySizeInBytes(texture.desc); + if (texture_size >= 1024ull * 1024ull) + { + ss << "\nMemory: " << ComputeTextureMemorySizeInBytes(texture.desc) / 1024.0f / 1024.0f << " MB"; + } + else + { + ss << "\nMemory: " << ComputeTextureMemorySizeInBytes(texture.desc) / 1024.0f << " KB"; + } + tooltiptext += ss.str(); + } + } + + textureSlotButton.SetTooltip(tooltiptext); + + }); textureSlotComboBox.SetSelected(0); textureSlotComboBox.SetTooltip("Choose the texture slot to modify."); AddWidget(&textureSlotComboBox); @@ -673,22 +712,14 @@ void MaterialWindow::Create(EditorComponent* _editor) params.extensions = wi::resourcemanager::GetSupportedImageExtensions(); wi::helper::FileDialog(params, [this, material, slot](std::string fileName) { wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { - wi::resourcemanager::Flags flags = wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA; - switch (slot) - { - case MaterialComponent::NORMALMAP: - case MaterialComponent::CLEARCOATNORMALMAP: - flags |= wi::resourcemanager::Flags::IMPORT_NORMALMAP; - break; - default: - break; - }; + wi::resourcemanager::Flags flags = material->GetTextureSlotResourceFlags(MaterialComponent::TEXTURESLOT(slot)); material->textures[slot].resource = wi::resourcemanager::Load(fileName, flags); material->textures[slot].name = fileName; material->SetDirty(); textureSlotLabel.SetText(wi::helper::GetFileNameFromPath(fileName)); - }); + textureSlotComboBox.SetSelected(slot); }); + }); } }); AddWidget(&textureSlotButton); @@ -724,6 +755,7 @@ void MaterialWindow::Create(EditorComponent* _editor) void MaterialWindow::SetEntity(Entity entity) { + bool changed = this->entity != entity; this->entity = entity; Scene& scene = editor->GetCurrentScene(); @@ -755,6 +787,7 @@ void MaterialWindow::SetEntity(Entity entity) windCheckBox.SetCheck(material->IsUsingWind()); doubleSidedCheckBox.SetCheck(material->IsDoubleSided()); outlineCheckBox.SetCheck(material->IsOutlineEnabled()); + preferUncompressedCheckBox.SetCheck(material->IsPreferUncompressedTexturesEnabled()); normalMapSlider.SetValue(material->normalMapStrength); roughnessSlider.SetValue(material->roughness); reflectanceSlider.SetValue(material->reflectance); @@ -857,6 +890,10 @@ void MaterialWindow::SetEntity(Entity entity) textureSlotButton.SetImage(material->textures[slot].resource); textureSlotLabel.SetText(wi::helper::GetFileNameFromPath(material->textures[slot].name)); textureSlotUvsetField.SetText(std::to_string(material->textures[slot].uvset)); + if (changed) + { + textureSlotComboBox.SetSelected(slot); + } } else { @@ -920,6 +957,7 @@ void MaterialWindow::ResizeLayout() add_right(windCheckBox); add_right(doubleSidedCheckBox); add_right(outlineCheckBox); + add_right(preferUncompressedCheckBox); add(shaderTypeComboBox); add(blendModeComboBox); add(shadingRateComboBox); diff --git a/Editor/MaterialWindow.h b/Editor/MaterialWindow.h index 2d3fc705f..1099569fa 100644 --- a/Editor/MaterialWindow.h +++ b/Editor/MaterialWindow.h @@ -20,6 +20,7 @@ public: wi::gui::CheckBox windCheckBox; wi::gui::CheckBox doubleSidedCheckBox; wi::gui::CheckBox outlineCheckBox; + wi::gui::CheckBox preferUncompressedCheckBox; wi::gui::ComboBox shaderTypeComboBox; wi::gui::ComboBox blendModeComboBox; wi::gui::ComboBox shadingRateComboBox; diff --git a/Editor/ModelImporter_GLTF.cpp b/Editor/ModelImporter_GLTF.cpp index 4f9adea3a..9ad163ee3 100644 --- a/Editor/ModelImporter_GLTF.cpp +++ b/Editor/ModelImporter_GLTF.cpp @@ -104,7 +104,7 @@ namespace tinygltf auto resource = wi::resourcemanager::Load( image->uri, - wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA, + wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA | wi::resourcemanager::Flags::IMPORT_DELAY, (const uint8_t*)bytes, (size_t)size ); @@ -114,10 +114,6 @@ namespace tinygltf return false; } - image->width = resource.GetTexture().desc.width; - image->height = resource.GetTexture().desc.height; - image->component = 4; - wi::resourcemanager::ResourceSerializer* seri = (wi::resourcemanager::ResourceSerializer*)userdata; seri->resources.push_back(resource); @@ -555,7 +551,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::BASECOLORMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::BASECOLORMAP].name = img.uri; material.textures[MaterialComponent::BASECOLORMAP].uvset = baseColorTexture->second.TextureTexCoord(); } @@ -568,7 +563,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::NORMALMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::NORMALMAP].name = img.uri; material.textures[MaterialComponent::NORMALMAP].uvset = normalTexture->second.TextureTexCoord(); } @@ -581,7 +575,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::SURFACEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SURFACEMAP].name = img.uri; material.textures[MaterialComponent::SURFACEMAP].uvset = metallicRoughnessTexture->second.TextureTexCoord(); } @@ -594,7 +587,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::EMISSIVEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::EMISSIVEMAP].name = img.uri; material.textures[MaterialComponent::EMISSIVEMAP].uvset = emissiveTexture->second.TextureTexCoord(); } @@ -607,7 +599,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::OCCLUSIONMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::OCCLUSIONMAP].name = img.uri; material.textures[MaterialComponent::OCCLUSIONMAP].uvset = occlusionTexture->second.TextureTexCoord(); material.SetOcclusionEnabled_Secondary(true); @@ -679,7 +670,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::TRANSMISSIONMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::TRANSMISSIONMAP].name = img.uri; material.textures[MaterialComponent::TRANSMISSIONMAP].uvset = (uint32_t)ext_transmission->second.Get("transmissionTexture").Get("texCoord").Get(); } @@ -703,7 +693,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::BASECOLORMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::BASECOLORMAP].name = img.uri; material.textures[MaterialComponent::BASECOLORMAP].uvset = (uint32_t)specularGlossinessWorkflow->second.Get("diffuseTexture").Get("texCoord").Get(); } @@ -717,7 +706,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::SURFACEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SURFACEMAP].name = img.uri; material.textures[MaterialComponent::SURFACEMAP].uvset = (uint32_t)specularGlossinessWorkflow->second.Get("specularGlossinessTexture").Get("texCoord").Get(); } @@ -771,7 +759,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::SHEENCOLORMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SHEENCOLORMAP].name = img.uri; material.textures[MaterialComponent::SHEENCOLORMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -791,7 +778,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::SHEENROUGHNESSMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SHEENROUGHNESSMAP].name = img.uri; material.textures[MaterialComponent::SHEENROUGHNESSMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -827,7 +813,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::CLEARCOATMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::CLEARCOATMAP].name = img.uri; material.textures[MaterialComponent::CLEARCOATMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -847,7 +832,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].name = img.uri; material.textures[MaterialComponent::CLEARCOATROUGHNESSMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -862,7 +846,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::CLEARCOATNORMALMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::CLEARCOATNORMALMAP].name = img.uri; material.textures[MaterialComponent::CLEARCOATNORMALMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -907,7 +890,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::SURFACEMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SURFACEMAP].name = img.uri; material.textures[MaterialComponent::SURFACEMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -922,7 +904,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::SPECULARMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SPECULARMAP].name = img.uri; material.textures[MaterialComponent::SPECULARMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -942,7 +923,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::SPECULARMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::SPECULARMAP].name = img.uri; material.textures[MaterialComponent::SPECULARMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -983,7 +963,6 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::ANISOTROPYMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::ANISOTROPYMAP].name = img.uri; material.textures[MaterialComponent::ANISOTROPYMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } @@ -1010,12 +989,12 @@ void ImportModel_GLTF(const std::string& fileName, Scene& scene) img_source = tex.extensions["KHR_texture_basisu"].Get("source").Get(); } auto& img = state.gltfModel.images[img_source]; - material.textures[MaterialComponent::ANISOTROPYMAP].resource = wi::resourcemanager::Load(img.uri); material.textures[MaterialComponent::ANISOTROPYMAP].name = img.uri; material.textures[MaterialComponent::ANISOTROPYMAP].uvset = (uint32_t)param.Get("texCoord").Get(); } } + material.CreateRenderData(); } // Create meshes: diff --git a/Editor/PaintToolWindow.cpp b/Editor/PaintToolWindow.cpp index 8bb951cb7..35f75449a 100644 --- a/Editor/PaintToolWindow.cpp +++ b/Editor/PaintToolWindow.cpp @@ -1340,7 +1340,7 @@ void PaintToolWindow::RecordHistory(bool start, CommandList cmd) ); } assert(cmd.IsValid()); - device->CopyResource(&newslot.texture, &editTexture.texture, cmd); + wi::renderer::CopyTexture2D(newslot.texture, 0, 0, 0, editTexture.texture, 0, cmd); // custom copy with format conversion capability! ReplaceEditTextureSlot(*material, newslot); } diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index d07d3111e..77c0b4e84 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wi::Archive versions +89: distortion particles must use the normal map slot from now on 88: volumetric clouds second layer 87: DDGI serialization: added grid_extents and smooth_backface 86: serialized volumetric clouds weather map, removed unused values and remapped values from VolumetricCloudParameters diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems b/WickedEngine/shaders/Shaders_SOURCE.vcxitems index cd18bbde7..57f9f8618 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems @@ -54,6 +54,18 @@ + + Compute + 4.0 + + + Compute + 4.0 + + + Compute + 4.0 + Compute Compute @@ -2622,6 +2634,9 @@ + + + @@ -2645,6 +2660,12 @@ + + + + + + diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters index 6ebc767c4..df47cfda9 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters @@ -34,6 +34,15 @@ {902ab028-5d33-4517-a981-4b5adc0f7031} + + {913dd8d5-7a4e-4a41-9737-4c690309165e} + + + {a0f04960-eae7-45c2-a6d8-c0c25ec3b333} + + + {2bb6f2d6-f3e1-4501-95bc-bd4a0ca64c4b} + @@ -1079,6 +1088,15 @@ CS + + CS + + + CS + + + CS + @@ -1201,5 +1219,32 @@ interop + + compressonator + + + compressonator + + + compressonator + + + ffx-fsr + + + ffx-fsr + + + ffx-shadows-dnsr + + + ffx-shadows-dnsr + + + ffx-shadows-dnsr + + + ffx-shadows-dnsr + \ No newline at end of file diff --git a/WickedEngine/shaders/blockcompressCS_BC1.hlsl b/WickedEngine/shaders/blockcompressCS_BC1.hlsl new file mode 100644 index 000000000..df2bbe9e0 --- /dev/null +++ b/WickedEngine/shaders/blockcompressCS_BC1.hlsl @@ -0,0 +1,224 @@ +#include "globals.hlsli" + +// Compressonator is better quality but slower: +#define USE_COMPRESSONATOR + +#ifdef USE_COMPRESSONATOR +#pragma dxc diagnostic push +#pragma dxc diagnostic ignored "-Wambig-lit-shift" +#pragma dxc diagnostic ignored "-Wunused-value" +#define ASPM_HLSL +#include "compressonator/bcn_common_kernel.h" +#pragma dxc diagnostic pop +#else +#include "BlockCompress.hlsli" +#endif // USE_COMPRESSONATOR + +#if !defined(BC3) && !defined(BC5) +#define BC1 +#endif // !BC3 && !BC5 + +Texture2D input : register(t0); + +#ifdef BC1 +RWTexture2D output : register(u0); +#endif // BC1 + +#ifdef BC3 +RWTexture2D output : register(u0); +#endif // BC3 + +#ifdef BC5 +RWTexture2D output : register(u0); +#endif // BC5 + +#if 0 +// Dithering is to fix bad-looking gradients in BC1 and BC3 RGB compression: +#define DITHER(color) (color + (dither((float2)DTid.xy) - 0.5f) / 64.0f) +#else +#define DITHER(color) (color) +#endif + +[numthreads(8, 8, 1)] +void main(uint3 DTid : SV_DispatchThreadID) +{ +#ifdef BC1 + float3 block[16]; +#endif // BC1 + +#ifdef BC3 + float3 block[16]; + float block_a[16]; +#endif // BC3 + +#ifdef BC5 + float block_u[16]; + float block_v[16]; +#endif // BC5 + + uint2 dim; + input.GetDimensions(dim.x, dim.y); + const float2 dim_rcp = rcp(dim); + const float2 uv = float2(DTid.xy * 4 + 1) * dim_rcp; + + //SUB-BLOCK/////////////////////////////////////////////////////////////////////// + + float4 red = input.GatherRed(sampler_linear_clamp, uv, int2(0, 0)); + float4 green = input.GatherGreen(sampler_linear_clamp, uv, int2(0, 0)); + float4 blue = input.GatherBlue(sampler_linear_clamp, uv, int2(0, 0)); + float4 alpha = input.GatherAlpha(sampler_linear_clamp, uv, int2(0, 0)); + +#if defined(BC1) || defined(BC3) + block[0] = DITHER(float3(red[3], green[3], blue[3])); + block[1] = DITHER(float3(red[2], green[2], blue[2])); + block[4] = DITHER(float3(red[0], green[0], blue[0])); + block[5] = DITHER(float3(red[1], green[1], blue[1])); +#endif // BC1 || BC3 + +#ifdef BC3 + block_a[0] = alpha[3]; + block_a[1] = alpha[2]; + block_a[4] = alpha[0]; + block_a[5] = alpha[1]; +#endif // BC3 + +#ifdef BC5 + block_u[0] = red[3]; + block_u[1] = red[2]; + block_u[4] = red[0]; + block_u[5] = red[1]; + block_v[0] = green[3]; + block_v[1] = green[2]; + block_v[4] = green[0]; + block_v[5] = green[1]; +#endif // BC5 + + //SUB-BLOCK/////////////////////////////////////////////////////////////////////// + + red = input.GatherRed(sampler_linear_clamp, uv, int2(2, 0)); + green = input.GatherGreen(sampler_linear_clamp, uv, int2(2, 0)); + blue = input.GatherBlue(sampler_linear_clamp, uv, int2(2, 0)); + alpha = input.GatherAlpha(sampler_linear_clamp, uv, int2(2, 0)); + +#if defined(BC1) || defined(BC3) + block[2] = DITHER(float3(red[3], green[3], blue[3])); + block[3] = DITHER(float3(red[2], green[2], blue[2])); + block[6] = DITHER(float3(red[0], green[0], blue[0])); + block[7] = DITHER(float3(red[1], green[1], blue[1])); +#endif // BC1 || BC3 + +#ifdef BC3 + block_a[2] = alpha[3]; + block_a[3] = alpha[2]; + block_a[6] = alpha[0]; + block_a[7] = alpha[1]; +#endif // BC3 + +#ifdef BC5 + block_u[2] = red[3]; + block_u[3] = red[2]; + block_u[6] = red[0]; + block_u[7] = red[1]; + block_v[2] = green[3]; + block_v[3] = green[2]; + block_v[6] = green[0]; + block_v[7] = green[1]; +#endif // BC5 + + //SUB-BLOCK/////////////////////////////////////////////////////////////////////// + + red = input.GatherRed(sampler_linear_clamp, uv, int2(0, 2)); + green = input.GatherGreen(sampler_linear_clamp, uv, int2(0, 2)); + blue = input.GatherBlue(sampler_linear_clamp, uv, int2(0, 2)); + alpha = input.GatherAlpha(sampler_linear_clamp, uv, int2(0, 2)); + +#if defined(BC1) || defined(BC3) + block[8] = DITHER(float3(red[3], green[3], blue[3])); + block[9] = DITHER(float3(red[2], green[2], blue[2])); + block[12] = DITHER(float3(red[0], green[0], blue[0])); + block[13] = DITHER(float3(red[1], green[1], blue[1])); +#endif // BC1 || BC3 + +#ifdef BC3 + block_a[8] = alpha[3]; + block_a[9] = alpha[2]; + block_a[12] = alpha[0]; + block_a[13] = alpha[1]; +#endif // BC3 + +#ifdef BC5 + block_u[8] = red[3]; + block_u[9] = red[2]; + block_u[12] = red[0]; + block_u[13] = red[1]; + block_v[8] = green[3]; + block_v[9] = green[2]; + block_v[12] = green[0]; + block_v[13] = green[1]; +#endif // BC5 + + //SUB-BLOCK/////////////////////////////////////////////////////////////////////// + + red = input.GatherRed(sampler_linear_clamp, uv, int2(2, 2)); + green = input.GatherGreen(sampler_linear_clamp, uv, int2(2, 2)); + blue = input.GatherBlue(sampler_linear_clamp, uv, int2(2, 2)); + alpha = input.GatherAlpha(sampler_linear_clamp, uv, int2(2, 2)); + +#if defined(BC1) || defined(BC3) + block[10] = DITHER(float3(red[3], green[3], blue[3])); + block[11] = DITHER(float3(red[2], green[2], blue[2])); + block[14] = DITHER(float3(red[0], green[0], blue[0])); + block[15] = DITHER(float3(red[1], green[1], blue[1])); +#endif // BC1 || BC3 + +#ifdef BC3 + block_a[10] = alpha[3]; + block_a[11] = alpha[2]; + block_a[14] = alpha[0]; + block_a[15] = alpha[1]; +#endif // BC3 + +#ifdef BC5 + block_u[10] = red[3]; + block_u[11] = red[2]; + block_u[14] = red[0]; + block_u[15] = red[1]; + block_v[10] = green[3]; + block_v[11] = green[2]; + block_v[14] = green[0]; + block_v[15] = green[1]; +#endif // BC5 + + //COMPRESS-WRITE/////////////////////////////////////////////////////////////////// + +#ifdef USE_COMPRESSONATOR + +#ifdef BC1 + output[DTid.xy] = CompressBlockBC1_UNORM(block, CMP_QUALITY2, /*isSRGB =*/ false); +#endif // BC1 + +#ifdef BC3 + output[DTid.xy] = CompressBlockBC3_UNORM(block, block_a, CMP_QUALITY2, /*isSRGB =*/ false); +#endif // BC3 + +#ifdef BC5 + output[DTid.xy] = CompressBlockBC5_UNORM(block_u, block_v, CMP_QUALITY2); +#endif // BC5 + +#else + +#ifdef BC1 + output[DTid.xy] = CompressBC1Block(block); +#endif // BC1 + +#ifdef BC3 + output[DTid.xy] = CompressBC3Block(block, block_a); +#endif // BC3 + +#ifdef BC5 + output[DTid.xy] = CompressBC5Block(block_u, block_v); +#endif // BC5 + +#endif // USE_COMPRESSONATOR + +} diff --git a/WickedEngine/shaders/blockcompressCS_BC3.hlsl b/WickedEngine/shaders/blockcompressCS_BC3.hlsl new file mode 100644 index 000000000..db372ceb5 --- /dev/null +++ b/WickedEngine/shaders/blockcompressCS_BC3.hlsl @@ -0,0 +1,2 @@ +#define BC3 +#include "blockcompressCS_BC1.hlsl" diff --git a/WickedEngine/shaders/blockcompressCS_BC5.hlsl b/WickedEngine/shaders/blockcompressCS_BC5.hlsl new file mode 100644 index 000000000..4eff07cd0 --- /dev/null +++ b/WickedEngine/shaders/blockcompressCS_BC5.hlsl @@ -0,0 +1,2 @@ +#define BC5 +#include "blockcompressCS_BC1.hlsl" diff --git a/WickedEngine/shaders/compressonator/bcn_common_api.h b/WickedEngine/shaders/compressonator/bcn_common_api.h new file mode 100644 index 000000000..676428664 --- /dev/null +++ b/WickedEngine/shaders/compressonator/bcn_common_api.h @@ -0,0 +1,1408 @@ +//=============================================================================== +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//=============================================================================== + +#ifndef BCN_COMMON_API_H_ +#define BCN_COMMON_API_H_ + +//=================================================================== +// NOTE: Do not use these API in production code, subject to changes +//=================================================================== + +#ifndef ASPM_GPU +#pragma warning(disable : 4244) +#pragma warning(disable : 4201) +#endif + +#include "common_def.h" + +#define CMP_MAX_16BITFLOAT 65504.0f +#define CMP_FLT_MAX 3.402823466e+38F +#define BC1ConstructColour(r, g, b) (((r) << 11) | ((g) << 5) | (b)) + + +#ifdef ASPM_HLSL +#define fabs(x) abs(x) +#endif + +CMP_STATIC CGU_FLOAT cmp_fabs(CMP_IN CGU_FLOAT x) +{ + return fabs(x); +} + +CMP_STATIC CGU_FLOAT cmp_linearToSrgbf(CMP_IN CGU_FLOAT Color) +{ + if (Color <= 0.0f) + return (0.0f); + if (Color >= 1.0f) + return (1.0f); + // standard : 0.0031308f + if (Color <= 0.00313066844250063) + return (Color * 12.92f); + return (pow(fabs(Color), 1.0f / 2.4f) * 1.055f - 0.055f); +} + +CMP_STATIC CGU_Vec3f cmp_linearToSrgb(CMP_IN CGU_Vec3f Color) +{ + Color.x = cmp_linearToSrgbf(Color.x); + Color.y = cmp_linearToSrgbf(Color.y); + Color.z = cmp_linearToSrgbf(Color.z); + return Color; +} + +CMP_STATIC CGU_FLOAT cmp_srgbToLinearf(CMP_IN CGU_FLOAT Color) +{ + if (Color <= 0.0f) + return (0.0f); + if (Color >= 1.0f) + return (1.0f); + // standard 0.04045f + if (Color <= 0.0404482362771082) + return (Color / 12.92f); + return pow((Color + 0.055f) / 1.055f, 2.4f); +} + +CMP_STATIC CGU_Vec3f cmp_srgbToLinear(CMP_IN CGU_Vec3f Color) +{ + Color.x = cmp_srgbToLinearf(Color.x); + Color.y = cmp_srgbToLinearf(Color.y); + Color.z = cmp_srgbToLinearf(Color.z); + return Color; +} + +CMP_STATIC CGU_Vec3f cmp_565ToLinear(CMP_IN CGU_UINT32 n565) +{ + CGU_UINT32 r0; + CGU_UINT32 g0; + CGU_UINT32 b0; + + r0 = ((n565 & 0xf800) >> 8); + g0 = ((n565 & 0x07e0) >> 3); + b0 = ((n565 & 0x001f) << 3); + + // Apply the lower bit replication to give full dynamic range (5,6,5) + r0 += (r0 >> 5); + g0 += (g0 >> 6); + b0 += (b0 >> 5); + + CGU_Vec3f LinearColor; + LinearColor.x = (CGU_FLOAT)r0; + LinearColor.y = (CGU_FLOAT)g0; + LinearColor.z = (CGU_FLOAT)b0; + + return LinearColor; +} + +CMP_STATIC CGU_UINT32 cmp_get2Bit32(CMP_IN CGU_UINT32 value, CMP_IN CGU_UINT32 indexPos) +{ + return (value >> (indexPos * 2)) & 0x3; +} + +CMP_STATIC CGU_UINT32 cmp_set2Bit32(CMP_IN CGU_UINT32 value, CMP_IN CGU_UINT32 indexPos) +{ + return ((value & 0x3) << (indexPos * 2)); +} + +CMP_STATIC CGU_UINT32 cmp_constructColor(CMP_IN CGU_UINT32 R, CMP_IN CGU_UINT32 G, CMP_IN CGU_UINT32 B) +{ + return (((R & 0x000000F8) << 8) | ((G & 0x000000FC) << 3) | ((B & 0x000000F8) >> 3)); +} + +CMP_STATIC CGU_FLOAT cmp_clampf(CMP_IN CGU_FLOAT v, CMP_IN CGU_FLOAT a, CMP_IN CGU_FLOAT b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + +CMP_STATIC CGU_Vec3f cmp_clampVec3f(CMP_IN CGU_Vec3f value, CMP_IN CGU_FLOAT minValue, CMP_IN CGU_FLOAT maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec3f revalue; + revalue.x = cmp_clampf(value.x, minValue, maxValue); + revalue.y = cmp_clampf(value.y, minValue, maxValue); + revalue.z = cmp_clampf(value.z, minValue, maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_saturate(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return saturate(value); +#else + return cmp_clampVec3f(value, 0.0f, 1.0f); +#endif +} + +static CGU_Vec3f cmp_powVec3f(CGU_Vec3f color, CGU_FLOAT ex) +{ +#ifdef ASPM_GPU + return pow(color, ex); +#else + CGU_Vec3f ColorSrgbPower; + ColorSrgbPower.x = pow(color.x, ex); + ColorSrgbPower.y = pow(color.y, ex); + ColorSrgbPower.z = pow(color.z, ex); + return ColorSrgbPower; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_minVec3f(CMP_IN CGU_Vec3f a, CMP_IN CGU_Vec3f b) +{ +#ifdef ASPM_HLSL + return min(a, b); +#endif + CGU_Vec3f res; + if (a.x < b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y < b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z < b.z) + res.z = a.z; + else + res.z = b.z; + return res; +} + +CMP_STATIC CGU_Vec3f cmp_maxVec3f(CMP_IN CGU_Vec3f a, CMP_IN CGU_Vec3f b) +{ +#ifdef ASPM_HLSL + return max(a, b); +#endif + CGU_Vec3f res; + if (a.x > b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y > b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z > b.z) + res.z = a.z; + else + res.z = b.z; + return res; +} + +inline CGU_Vec3f cmp_min3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ +#ifdef ASPM_GPU + return min(value1, value2); +#else + CGU_Vec3f res; + res.x = CMP_MIN(value1.x, value2.x); + res.y = CMP_MIN(value1.y, value2.y); + res.z = CMP_MIN(value1.z, value2.z); + return res; +#endif +} + +inline CGU_Vec3f cmp_max3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ +#ifdef ASPM_GPU + return max(value1, value2); +#else + CGU_Vec3f res; + res.x = CMP_MAX(value1.x, value2.x); + res.y = CMP_MAX(value1.y, value2.y); + res.z = CMP_MAX(value1.z, value2.z); + return res; +#endif +} + +CMP_STATIC CGU_FLOAT cmp_minf(CMP_IN CGU_FLOAT a, CMP_IN CGU_FLOAT b) +{ + return a < b ? a : b; +} + +CMP_STATIC CGU_FLOAT cmp_maxf(CMP_IN CGU_FLOAT a, CMP_IN CGU_FLOAT b) +{ + return a > b ? a : b; +} + +CMP_STATIC CGU_FLOAT cmp_floor(CMP_IN CGU_FLOAT value) +{ + return floor(value); +} + +CMP_STATIC CGU_Vec3f cmp_floorVec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GPU + return floor(value); +#else + CGU_Vec3f revalue; + revalue.x = floor(value.x); + revalue.y = floor(value.y); + revalue.z = floor(value.z); + return revalue; +#endif +} + +#ifndef ASPM_OPENCL + +//======================================================= +// COMMON GPU & CPU API +//======================================================= + +//====================== +// implicit vector cast +//====================== +CMP_STATIC CGU_Vec4i cmp_castimp(CGU_Vec4ui v1) +{ +#ifdef ASPM_HLSL + return (v1); +#else + return (v1.x, v1.y, v1.z, v1.w); +#endif +} + +CMP_STATIC CGU_Vec3i cmp_castimp(CGU_Vec3ui v1) +{ +#ifdef ASPM_HLSL + return (v1); +#else + return (v1.x, v1.y, v1.z); +#endif +} + +//====================== +// Min / Max +//====================== + +CMP_STATIC CGU_UINT8 cmp_min8(CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + return a < b ? a : b; +} + +CMP_STATIC CGU_UINT8 cmp_max8(CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + return a > b ? a : b; +} + +CMP_STATIC CGU_UINT32 cmp_mini(CMP_IN CGU_UINT32 a, CMP_IN CGU_UINT32 b) +{ + return (a < b) ? a : b; +} + +CMP_STATIC CGU_UINT32 cmp_maxi(CMP_IN CGU_UINT32 a, CMP_IN CGU_UINT32 b) +{ + return (a > b) ? a : b; +} + +CMP_STATIC CGU_FLOAT cmp_max3(CMP_IN CGU_FLOAT i, CMP_IN CGU_FLOAT j, CMP_IN CGU_FLOAT k) +{ +#ifdef ASPM_GLSL + return max3(i, j, k); +#else + CGU_FLOAT max = i; + + if (max < j) + max = j; + + if (max < k) + max = k; + + return (max); +#endif +} + + +CMP_STATIC CGU_Vec4ui cmp_minVec4ui(CMP_IN CGU_Vec4ui a, CMP_IN CGU_Vec4ui b) +{ + //#ifdef ASPM_HLSL + // return min(a, b); + //#endif + //#ifndef ASPM_GPU + CGU_Vec4ui res; + if (a.x < b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y < b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z < b.z) + res.z = a.z; + else + res.z = b.z; + if (a.w < b.w) + res.w = a.w; + else + res.w = b.w; + return res; + //#endif +} + +CMP_STATIC CGU_Vec4ui cmp_maxVec4ui(CMP_IN CGU_Vec4ui a, CMP_IN CGU_Vec4ui b) +{ + //#ifdef ASPM_HLSL + // return max(a, b); + //#endif + //#ifndef ASPM_GPU + CGU_Vec4ui res; + if (a.x > b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y > b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z > b.z) + res.z = a.z; + else + res.z = b.z; + if (a.w > b.w) + res.w = a.w; + else + res.w = b.w; + return res; + //#endif +} + +//====================== +// Clamps +//====================== + +CMP_STATIC CGU_UINT32 cmp_clampui32(CMP_IN CGU_UINT32 v, CMP_IN CGU_UINT32 a, CMP_IN CGU_UINT32 b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + + +// Test Ref:https://en.wikipedia.org/wiki/Half-precision_floating-point_format +// Half (in Hex) Float Comment +// --------------------------------------------------------------------------- +// 0001 (approx) = 0.000000059604645 smallest positive subnormal number +// 03ff (approx) = 0.000060975552 largest subnormal number +// 0400 (approx) = 0.00006103515625 smallest positive normal number +// 7bff (approx) = 65504 largest normal number +// 3bff (approx) = 0.99951172 largest number less than one +// 3c00 (approx) = 1.00097656 smallest number larger than one +// 3555 = 0.33325195 the rounding of 1/3 to nearest +// c000 = ?2 +// 8000 = -0 +// 0000 = 0 +// 7c00 = infinity +// fc00 = infinity +// Half Float Math + +CMP_STATIC CGU_FLOAT HalfToFloat(CGU_UINT32 h) +{ +#if defined(ASPM_GPU) + CGU_FLOAT f = min16float((float)(h)); + return f; +#else + union FP32 + { + CGU_UINT32 u; + CGU_FLOAT f; + }; + + const FP32 magic = {(254 - 15) << 23}; + const FP32 was_infnan = {(127 + 16) << 23}; + + FP32 o; + o.u = (h & 0x7fff) << 13; // exponent/mantissa bits + o.f *= magic.f; // exponent adjust + if (o.f >= was_infnan.f) // check Inf/NaN + o.u |= 255 << 23; + o.u |= (h & 0x8000) << 16; // sign bit + return o.f; +#endif +} + +// From BC6HEcode.hlsl + +CMP_STATIC CGU_FLOAT cmp_half2float1(CGU_UINT32 Value) +{ + CGU_UINT32 Mantissa = (CGU_UINT32)(Value & 0x03FF); + + CGU_UINT32 Exponent; + if ((Value & 0x7C00) != 0) // The value is normalized + { + Exponent = (CGU_UINT32)((Value >> 10) & 0x1F); + } + else if (Mantissa != 0) // The value is denormalized + { + // Normalize the value in the resulting float + Exponent = 1; + + do + { + Exponent--; + Mantissa <<= 1; + } while ((Mantissa & 0x0400) == 0); + + Mantissa &= 0x03FF; + } + else // The value is zero + { + Exponent = (CGU_UINT32)(-112); + } + + CGU_UINT32 Result = ((Value & 0x8000) << 16) | // Sign + ((Exponent + 112) << 23) | // Exponent + (Mantissa << 13); // Mantissa + + return CGU_FLOAT(Result); +} + +CMP_STATIC CGU_Vec3f cmp_half2floatVec3(CGU_Vec3ui color_h) +{ + //uint3 sign = color_h & 0x8000; + //uint3 expo = color_h & 0x7C00; + //uint3 base = color_h & 0x03FF; + //return ( expo == 0 ) ? asfloat( ( sign << 16 ) | asuint( float3(base) / 16777216 ) ) //16777216 = 2^24 + // : asfloat( ( sign << 16 ) | ( ( ( expo + 0x1C000 ) | base ) << 13 ) ); //0x1C000 = 0x1FC00 - 0x3C00 + + return CGU_Vec3f(cmp_half2float1(color_h.x), cmp_half2float1(color_h.y), cmp_half2float1(color_h.z)); +} + +CMP_STATIC CGU_UINT16 FloatToHalf(CGU_FLOAT value) +{ +#if defined(ASPM_GPU) + return 0; +#else + union FP32 + { + CGU_UINT16 u; + float f; + struct + { + CGU_UINT32 Mantissa : 23; + CGU_UINT32 Exponent : 8; + CGU_UINT32 Sign : 1; + }; + }; + + union FP16 + { + CGU_UINT16 u; + struct + { + CGU_UINT32 Mantissa : 10; + CGU_UINT32 Exponent : 5; + CGU_UINT32 Sign : 1; + }; + }; + + FP16 o = {0}; + FP32 f; + f.f = value; + + // Based on ISPC reference code (with minor modifications) + if (f.Exponent == 0) // Signed zero/denormal (which will underflow) + o.Exponent = 0; + else if (f.Exponent == 255) // Inf or NaN (all exponent bits set) + { + o.Exponent = 31; + o.Mantissa = f.Mantissa ? 0x200 : 0; // NaN->qNaN and Inf->Inf + } + else // Normalized number + { + // Exponent unbias the single, then bias the halfp + int newexp = f.Exponent - 127 + 15; + if (newexp >= 31) // Overflow, return signed infinity + o.Exponent = 31; + else if (newexp <= 0) // Underflow + { + if ((14 - newexp) <= 24) // Mantissa might be non-zero + { + CGU_UINT32 mant = f.Mantissa | 0x800000; // Hidden 1 bit + o.Mantissa = mant >> (14 - newexp); + if ((mant >> (13 - newexp)) & 1) // Check for rounding + o.u++; // Round, might overflow into exp bit, but this is OK + } + } + else + { + o.Exponent = newexp; + o.Mantissa = f.Mantissa >> 13; + if (f.Mantissa & 0x1000) // Check for rounding + o.u++; // Round, might overflow to inf, this is OK + } + } + + o.Sign = f.Sign; + return o.u; +#endif +} + +CMP_STATIC CGU_UINT32 cmp_float2halfui(CGU_FLOAT f) +{ + CGU_UINT32 Result; + + CGU_UINT32 IValue = CGU_UINT32(f); + CGU_UINT32 Sign = (IValue & 0x80000000U) >> 16U; + IValue = IValue & 0x7FFFFFFFU; + + if (IValue > 0x47FFEFFFU) + { + // The number is too large to be represented as a half. Saturate to infinity. + Result = 0x7FFFU; + } + else + { + if (IValue < 0x38800000U) + { + // The number is too small to be represented as a normalized half. + // Convert it to a denormalized value. + CGU_UINT32 Shift = 113U - (IValue >> 23U); + IValue = (0x800000U | (IValue & 0x7FFFFFU)) >> Shift; + } + else + { + // Rebias the exponent to represent the value as a normalized half. + IValue += 0xC8000000U; + } + + Result = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU; + } + return (Result | Sign); +} + +CMP_STATIC CGU_Vec3ui cmp_float2half(CGU_Vec3f endPoint_f) +{ + return CGU_Vec3ui(cmp_float2halfui(endPoint_f.x), cmp_float2halfui(endPoint_f.y), cmp_float2halfui(endPoint_f.z)); +} + +CMP_STATIC CGU_UINT32 cmp_float2half1(CGU_FLOAT f) +{ + CGU_UINT32 Result; + + CGU_UINT32 IValue = CGU_UINT32(f); //asuint(f); + CGU_UINT32 Sign = (IValue & 0x80000000U) >> 16U; + IValue = IValue & 0x7FFFFFFFU; + + if (IValue > 0x47FFEFFFU) + { + // The number is too large to be represented as a half. Saturate to infinity. + Result = 0x7FFFU; + } + else + { + if (IValue < 0x38800000U) + { + // The number is too small to be represented as a normalized half. + // Convert it to a denormalized value. + CGU_UINT32 Shift = 113U - (IValue >> 23U); + IValue = (0x800000U | (IValue & 0x7FFFFFU)) >> Shift; + } + else + { + // Rebias the exponent to represent the value as a normalized half. + IValue += 0xC8000000U; + } + + Result = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU; + } + return (Result | Sign); +} + +CMP_STATIC CGU_Vec3ui cmp_float2halfVec3(CGU_Vec3f endPoint_f) +{ + return CGU_Vec3ui(cmp_float2half1(endPoint_f.x), cmp_float2half1(endPoint_f.y), cmp_float2half1(endPoint_f.z)); +} + +CMP_STATIC CGU_FLOAT cmp_f32tof16(CMP_IN CGU_FLOAT value) +{ +#ifdef ASPM_GLSL + return packHalf2x16(CGU_Vec2f(value.x, 0.0)); +#endif +#ifdef ASPM_HLSL + return f32tof16(value); +#endif +#ifndef ASPM_GPU + return FloatToHalf(value); +#endif +} + +CMP_STATIC CGU_Vec3f cmp_f32tof16(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GLSL + return CGU_Vec3f(packHalf2x16(CGU_Vec2f(value.x, 0.0)), packHalf2x16(CGU_Vec2f(value.y, 0.0)), packHalf2x16(CGU_Vec2f(value.z, 0.0))); +#endif +#ifdef ASPM_HLSL + return f32tof16(value); +#endif +#ifndef ASPM_GPU + CGU_Vec3f res; + res.x = FloatToHalf(value.x); + res.y = FloatToHalf(value.y); + res.z = FloatToHalf(value.z); + return res; +#endif +} + +CMP_STATIC CGU_FLOAT cmp_f16tof32(CGU_UINT32 value) +{ +#ifdef ASPM_GLSL + return unpackHalf2x16(value).x; +#endif +#ifdef ASPM_HLSL + return f16tof32(value); +#endif +#ifndef ASPM_GPU + return HalfToFloat(value); +#endif +} + +CMP_STATIC CGU_Vec3f cmp_f16tof32(CGU_Vec3ui value) +{ +#ifdef ASPM_GLSL + return CGU_Vec3f(unpackHalf2x16(value.x).x, unpackHalf2x16(value.y).x, unpackHalf2x16(value.z).x); +#endif +#ifdef ASPM_HLSL + return f16tof32(value); +#endif +#ifndef ASPM_GPU + CGU_Vec3f res; + res.x = HalfToFloat(value.x); + res.y = HalfToFloat(value.y); + res.z = HalfToFloat(value.z); + return res; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_f16tof32(CGU_Vec3f value) +{ +#ifdef ASPM_GLSL + return CGU_Vec3f(unpackHalf2x16(value.x).x, unpackHalf2x16(value.y).x, unpackHalf2x16(value.z).x); +#endif +#ifdef ASPM_HLSL + return f16tof32(value); +#endif +#ifndef ASPM_GPU + CGU_Vec3f res; + res.x = HalfToFloat((CGU_UINT32)value.x); + res.y = HalfToFloat((CGU_UINT32)value.y); + res.z = HalfToFloat((CGU_UINT32)value.z); + return res; +#endif +} + +CMP_STATIC void cmp_swap(CMP_INOUT CGU_Vec3f CMP_REFINOUT a, CMP_INOUT CGU_Vec3f CMP_REFINOUT b) +{ + CGU_Vec3f tmp = a; + a = b; + b = tmp; +} + +CMP_STATIC void cmp_swap(CMP_INOUT CGU_FLOAT CMP_REFINOUT a, CMP_INOUT CGU_FLOAT CMP_REFINOUT b) +{ + CGU_FLOAT tmp = a; + a = b; + b = tmp; +} + +CMP_STATIC void cmp_swap(CMP_INOUT CGU_Vec3i CMP_REFINOUT lhs, CMP_INOUT CGU_Vec3i CMP_REFINOUT rhs) // valided with msc code +{ + CGU_Vec3i tmp = lhs; + lhs = rhs; + rhs = tmp; +} + +CMP_STATIC CGU_INT cmp_dotVec2i(CMP_IN CGU_Vec2i value1, CMP_IN CGU_Vec2i value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y); +#endif +} + +CMP_STATIC CGU_FLOAT cmp_dotVec3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z); +#endif +} + +CMP_STATIC CGU_UINT32 cmp_dotVec3ui(CMP_IN CGU_Vec3ui value1, CMP_IN CGU_Vec3ui value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z); +#endif +} + +CMP_STATIC CGU_UINT32 cmp_dotVec4i(CMP_IN CGU_Vec4i value1, CMP_IN CGU_Vec4i value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z) + (value1.w * value2.w); +#endif +} + +CMP_STATIC CGU_UINT32 cmp_dotVec4ui(CMP_IN CGU_Vec4ui value1, CMP_IN CGU_Vec4ui value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z) + (value1.w * value2.w); +#endif +} + +CMP_STATIC CGU_Vec3f cmp_clampVec3fi(CMP_IN CGU_Vec3f value, CMP_IN CGU_INT minValue, CMP_IN CGU_INT maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec3f revalue; + revalue.x = cmp_clampf(value.x, (CGU_FLOAT)minValue, (CGU_FLOAT)maxValue); + revalue.y = cmp_clampf(value.y, (CGU_FLOAT)minValue, (CGU_FLOAT)maxValue); + revalue.z = cmp_clampf(value.z, (CGU_FLOAT)minValue, (CGU_FLOAT)maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec4ui cmp_clampVec4ui(CMP_IN CGU_Vec4ui value, CMP_IN CGU_UINT32 minValue, CMP_IN CGU_UINT32 maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec4ui revalue; + revalue.x = cmp_clampui32(value.x, minValue, maxValue); + revalue.y = cmp_clampui32(value.y, minValue, maxValue); + revalue.z = cmp_clampui32(value.z, minValue, maxValue); + revalue.w = cmp_clampui32(value.w, minValue, maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec4f cmp_clampVec4f(CMP_IN CGU_Vec4f value, CMP_IN CGU_FLOAT minValue, CMP_IN CGU_FLOAT maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec4f revalue; + revalue.x = cmp_clampf(value.x, minValue, maxValue); + revalue.y = cmp_clampf(value.y, minValue, maxValue); + revalue.z = cmp_clampf(value.z, minValue, maxValue); + revalue.w = cmp_clampf(value.w, minValue, maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_clamp3Vec3f(CMP_IN CGU_Vec3f value, CMP_IN CGU_Vec3f minValue, CMP_IN CGU_Vec3f maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec3f revalue; + revalue.x = cmp_clampf(value.x, minValue.x, maxValue.x); + revalue.y = cmp_clampf(value.y, minValue.y, maxValue.y); + revalue.z = cmp_clampf(value.z, minValue.z, maxValue.z); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_exp2(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GPU + return exp2(value); +#else + CGU_Vec3f revalue; + revalue.x = exp2(value.x); + revalue.y = exp2(value.y); + revalue.z = exp2(value.z); + return revalue; +#endif +} + + +CMP_STATIC CGU_Vec3f cmp_roundVec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return round(value); +#endif +#ifndef ASPM_HLSL + CGU_Vec3f res; + res.x = round(value.x); + res.y = round(value.y); + res.z = round(value.z); + return res; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_log2Vec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GPU + return log2(value); +#else + CGU_Vec3f res; + res.x = log2(value.x); + res.y = log2(value.y); + res.z = log2(value.z); + return res; +#endif +} + +// used in BC1 LowQuality code +CMP_STATIC CGU_FLOAT cmp_saturate(CMP_IN CGU_FLOAT value) +{ +#ifdef ASPM_HLSL + return saturate(value); +#else + return cmp_clampf(value, 0.0f, 1.0f); +#endif +} + +CMP_STATIC CGU_FLOAT cmp_rcp(CMP_IN CGU_FLOAT det) +{ +#ifdef ASPM_HLSL + return rcp(det); +#else + if (det > 0.0f) + return (1 / det); + else + return 0.0f; +#endif +} + +CMP_STATIC CGU_UINT32 cmp_Get4BitIndexPos(CMP_IN CGU_FLOAT indexPos, CMP_IN CGU_FLOAT endPoint0Pos, CMP_IN CGU_FLOAT endPoint1Pos) +{ + CGU_FLOAT r = (indexPos - endPoint0Pos) / (endPoint1Pos - endPoint0Pos); + return cmp_clampui32(CGU_UINT32(r * 14.93333f + 0.03333f + 0.5f), 0, 15); +} + +// Calculate Mean Square Least Error (MSLE) for 2 Vectors +CMP_STATIC CGU_FLOAT cmp_CalcMSLE(CMP_IN CGU_Vec3f a, CMP_IN CGU_Vec3f b) +{ + CGU_Vec3f err = cmp_log2Vec3f((b + 1.0f) / (a + 1.0f)); + err = err * err; + return err.x + err.y + err.z; +} + + + +// Compute Endpoints (min/max) bounding box +CMP_STATIC void cmp_GetTexelMinMax(CMP_IN CGU_Vec3f texels[16], CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMin, CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMax) +{ + blockMin = texels[0]; + blockMax = texels[0]; + for (CGU_UINT32 i = 1u; i < 16u; ++i) + { + blockMin = cmp_minVec3f(blockMin, texels[i]); + blockMax = cmp_maxVec3f(blockMax, texels[i]); + } +} + +// Refine Endpoints (min/max) by insetting bounding box in log2 RGB space +CMP_STATIC void cmp_RefineMinMaxAsLog2(CMP_IN CGU_Vec3f texels[16], CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMin, CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMax) +{ + CGU_Vec3f refinedBlockMin = blockMax; + CGU_Vec3f refinedBlockMax = blockMin; + for (CGU_UINT32 i = 0u; i < 16u; ++i) + { + refinedBlockMin = cmp_minVec3f(refinedBlockMin, texels[i] == blockMin ? refinedBlockMin : texels[i]); + refinedBlockMax = cmp_maxVec3f(refinedBlockMax, texels[i] == blockMax ? refinedBlockMax : texels[i]); + } + + CGU_Vec3f logBlockMax = cmp_log2Vec3f(blockMax + 1.0f); + CGU_Vec3f logBlockMin = cmp_log2Vec3f(blockMin + 1.0f); + + CGU_Vec3f logRefinedBlockMax = cmp_log2Vec3f(refinedBlockMax + 1.0f); + CGU_Vec3f logRefinedBlockMin = cmp_log2Vec3f(refinedBlockMin + 1.0f); + CGU_Vec3f logBlockMaxExt = (logBlockMax - logBlockMin) * (1.0f / 32.0f); + + logBlockMin += cmp_minVec3f(logRefinedBlockMin - logBlockMin, logBlockMaxExt); + logBlockMax -= cmp_minVec3f(logBlockMax - logRefinedBlockMax, logBlockMaxExt); + + blockMin = cmp_exp2(logBlockMin) - 1.0f; + blockMax = cmp_exp2(logBlockMax) - 1.0f; +} + +// Refine Endpoints (min/max) by Least Squares Optimization +CMP_STATIC void cmp_RefineMinMaxAs16BitLeastSquares(CMP_IN CGU_Vec3f texels[16], + CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMin, + CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMax) +{ + CGU_Vec3f blockDir = blockMax - blockMin; + blockDir = blockDir / (blockDir.x + blockDir.y + blockDir.z); + + CGU_FLOAT endPoint0Pos = cmp_f32tof16(cmp_dotVec3f(blockMin, blockDir)); + CGU_FLOAT endPoint1Pos = cmp_f32tof16(cmp_dotVec3f(blockMax, blockDir)); + + CGU_Vec3f alphaTexelSum = 0.0f; + CGU_Vec3f betaTexelSum = 0.0f; + CGU_FLOAT alphaBetaSum = 0.0f; + CGU_FLOAT alphaSqSum = 0.0f; + CGU_FLOAT betaSqSum = 0.0f; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + CGU_FLOAT texelPos = cmp_f32tof16(cmp_dotVec3f(texels[i], blockDir)); + CGU_UINT32 texelIndex = cmp_Get4BitIndexPos(texelPos, endPoint0Pos, endPoint1Pos); + + CGU_FLOAT beta = cmp_saturate(texelIndex / 15.0f); + CGU_FLOAT alpha = 1.0f - beta; + + CGU_Vec3f texelF16; + texelF16.x = cmp_f32tof16(texels[i].x); + texelF16.y = cmp_f32tof16(texels[i].y); + texelF16.z = cmp_f32tof16(texels[i].z); + + alphaTexelSum += texelF16 * alpha; + betaTexelSum += texelF16 * beta; + + alphaBetaSum += alpha * beta; + + alphaSqSum += alpha * alpha; + betaSqSum += beta * beta; + } + + CGU_FLOAT det = alphaSqSum * betaSqSum - alphaBetaSum * alphaBetaSum; + + if (abs(det) > 0.00001f) + { + CGU_FLOAT detRcp = cmp_rcp(det); + blockMin = cmp_clampVec3f((alphaTexelSum * betaSqSum - betaTexelSum * alphaBetaSum) * detRcp, 0.0f, CMP_MAX_16BITFLOAT); + blockMax = cmp_clampVec3f((betaTexelSum * alphaSqSum - alphaTexelSum * alphaBetaSum) * detRcp, 0.0f, CMP_MAX_16BITFLOAT); + blockMin = cmp_f16tof32(blockMin); + blockMax = cmp_f16tof32(blockMax); + } +} + +//============================================================================================= + +CMP_STATIC CGU_Vec3f cmp_fabsVec3f(CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return abs(value); +#else + CGU_Vec3f res; + res.x = abs(value.x); + res.y = abs(value.y); + res.z = abs(value.z); + return res; +#endif +} + + +CMP_STATIC CGU_UINT32 cmp_constructColor(CMP_IN CGU_Vec3ui EndPoints) +{ + return (((EndPoints.r & 0x000000F8) << 8) | ((EndPoints.g & 0x000000FC) << 3) | ((EndPoints.b & 0x000000F8) >> 3)); +} + +CMP_STATIC CGU_UINT32 cmp_constructColorBGR(CMP_IN CGU_Vec3f EndPoints) +{ + return (((CGU_UINT32(EndPoints.b) & 0x000000F8) << 8) | ((CGU_UINT32(EndPoints.g) & 0x000000FC) << 3) | ((CGU_UINT32(EndPoints.r) & 0x000000F8) >> 3)); +} + +CMP_STATIC CGU_FLOAT cmp_mod(CMP_IN CGU_FLOAT value, CMP_IN CGU_FLOAT modval) +{ +#ifdef ASPM_GLSL + return mod(value, modval); +#endif + return fmod(value, modval); +} + +CMP_STATIC CGU_Vec3f cmp_truncVec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return trunc(value); +#else + CGU_Vec3f res; + res.x = trunc(value.x); + res.y = trunc(value.y); + res.z = trunc(value.z); + return res; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_ceilVec3f(CMP_IN CGU_Vec3f value) +{ + CGU_Vec3f res; + res.x = ceil(value.x); + res.y = ceil(value.y); + res.z = ceil(value.z); + return res; +} + +CMP_STATIC CGU_FLOAT cmp_sqrt(CGU_FLOAT value) +{ + return sqrt(value); +} + +// Computes inverse square root over an implementation-defined range. The maximum error is implementation-defined. +CMP_STATIC CGV_FLOAT cmp_rsqrt(CGV_FLOAT f) +{ + CGV_FLOAT sf = sqrt(f); + if (sf != 0) + return 1 / sqrt(f); + else + return 0.0f; +} + +// Common to BC7 API ------------------------------------------------------------------------------------------------------------------------ + +// valid bit range is 0..8 for mode 1 +CMP_STATIC INLINE CGU_UINT32 cmp_shift_right_uint32(CMP_IN CGU_UINT32 v, CMP_IN CGU_INT bits) +{ + return v >> bits; // (perf warning expected) +} + +CMP_STATIC INLINE CGU_INT cmp_clampi(CMP_IN CGU_INT value, CMP_IN CGU_INT low, CMP_IN CGU_INT high) +{ + if (value < low) + return low; + else if (value > high) + return high; + return value; +} + +CMP_STATIC INLINE CGU_INT32 cmp_clampi32(CMP_IN CGU_INT32 value, CMP_IN CGU_INT32 low, CMP_IN CGU_INT32 high) +{ + if (value < low) + value = low; + else if (value > high) + value = high; + return value; +} + +CMP_STATIC CGV_FLOAT cmp_dot4f(CMP_IN CGV_Vec4f value1, CMP_IN CGV_Vec4f value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z) + (value1.w * value2.w); +#endif +} + +CMP_STATIC INLINE void cmp_set_vec4f(CMP_INOUT CGU_Vec4f CMP_REFINOUT pV, CMP_IN CGU_FLOAT x, CMP_IN CGU_FLOAT y, CMP_IN CGU_FLOAT z, CMP_IN CGU_FLOAT w) +{ + pV[0] = x; + pV[1] = y; + pV[2] = z; + pV[3] = w; +} + +CMP_STATIC INLINE void cmp_set_vec4ui(CGU_Vec4ui CMP_REFINOUT pV, CMP_IN CGU_UINT8 x, CMP_IN CGU_UINT8 y, CMP_IN CGU_UINT8 z, CMP_IN CGU_UINT8 w) +{ + pV[0] = x; + pV[1] = y; + pV[2] = z; + pV[3] = w; +} + +CMP_STATIC inline void cmp_set_vec4ui_clamped(CGU_Vec4ui CMP_REFINOUT pRes, CMP_IN CGU_INT32 r, CMP_IN CGU_INT32 g, CMP_IN CGU_INT32 b, CMP_IN CGU_INT32 a) +{ + pRes[0] = (CGU_UINT8)cmp_clampi32(r, 0, 255); + pRes[1] = (CGU_UINT8)cmp_clampi32(g, 0, 255); + pRes[2] = (CGU_UINT8)cmp_clampi32(b, 0, 255); + pRes[3] = (CGU_UINT8)cmp_clampi32(a, 0, 255); +} + +CMP_STATIC inline CGU_Vec4f cmp_clampNorm4f(CMP_IN CGU_Vec4f pV) +{ + CGU_Vec4f res; + res[0] = cmp_clampf(pV[0], 0.0f, 1.0f); + res[1] = cmp_clampf(pV[1], 0.0f, 1.0f); + res[2] = cmp_clampf(pV[2], 0.0f, 1.0f); + res[3] = cmp_clampf(pV[3], 0.0f, 1.0f); + return res; +} + +CMP_STATIC INLINE CGU_Vec4f cmp_vec4ui_to_vec4f(CMP_IN CGU_Vec4ui pC) +{ + CGU_Vec4f res; + cmp_set_vec4f(res, (CGU_FLOAT)pC[0], (CGU_FLOAT)pC[1], (CGU_FLOAT)pC[2], (CGU_FLOAT)pC[3]); + return res; +} + +CMP_STATIC INLINE void cmp_normalize(CGU_Vec4f CMP_REFINOUT pV) +{ + CGU_FLOAT s = cmp_dot4f(pV, pV); + if (s != 0.0f) + { + s = 1.0f / cmp_sqrt(s); + pV *= s; + } +} + +CMP_STATIC INLINE CGV_FLOAT cmp_squaref(CMP_IN CGV_FLOAT v) +{ + return v * v; +} + +CMP_STATIC INLINE CGU_INT cmp_squarei(CMP_IN CGU_INT i) +{ + return i * i; +} + +CMP_STATIC CGU_UINT8 cmp_clampui8(CMP_IN CGU_UINT8 v, CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + +CMP_STATIC CGU_INT32 cmp_abs32(CMP_IN CGU_INT32 v) +{ + CGU_UINT32 msk = v >> 31; + return (v ^ msk) - msk; +} + +CMP_STATIC void cmp_swap32(CMP_INOUT CGU_UINT32 CMP_REFINOUT a, CMP_INOUT CGU_UINT32 CMP_REFINOUT b) +{ + CGU_UINT32 t = a; + a = b; + b = t; +} + +// Computes inverse square root over an implementation-defined range. The maximum error is implementation-defined. +CMP_STATIC CGV_FLOAT cmp_Image_rsqrt(CMP_IN CGV_FLOAT f) +{ + CGV_FLOAT sf = sqrt(f); + if (sf != 0) + return 1 / sqrt(f); + else + return 0.0f; +} + +CMP_STATIC void cmp_pack4bitindex32(CMP_INOUT CGU_UINT32 packed_index[2], CMP_IN CGU_UINT32 src_index[16]) +{ + // Converts from unpacked index to packed index + packed_index[0] = 0x0000; + packed_index[1] = 0x0000; + CGU_UINT32 shift = 0; // was CGU_UINT8 + for (CGU_INT k = 0; k < 8; k++) + { + packed_index[0] |= (CGU_UINT32)(src_index[k] & 0x0F) << shift; + packed_index[1] |= (CGU_UINT32)(src_index[k + 8] & 0x0F) << shift; + shift += 4; + } +} + +CMP_STATIC void cmp_pack4bitindex(CMP_INOUT CGU_UINT32 packed_index[2], CMP_IN CGU_UINT8 src_index[16]) +{ + // Converts from unpacked index to packed index + packed_index[0] = 0x0000; + packed_index[1] = 0x0000; + CGU_UINT32 shift = 0; // was CGU_UINT8 + for (CGU_INT k = 0; k < 8; k++) + { + packed_index[0] |= (CGU_UINT32)(src_index[k] & 0x0F) << shift; + packed_index[1] |= (CGU_UINT32)(src_index[k + 8] & 0x0F) << shift; + shift += 4; + } +} + +CMP_STATIC INLINE CGU_INT cmp_expandbits(CMP_IN CGU_INT v, CMP_IN CGU_INT bits) +{ + CGU_INT vv = v << (8 - bits); + return vv + cmp_shift_right_uint32(vv, bits); +} + +// This code need further improvements and investigation +CMP_STATIC INLINE CGU_UINT8 cmp_ep_find_floor2(CMP_IN CGV_FLOAT v, CMP_IN CGU_UINT8 bits, CMP_IN CGU_UINT8 use_par, CMP_IN CGU_UINT8 odd) +{ + CGU_UINT8 i1 = 0; + CGU_UINT8 i2 = 1 << (bits - use_par); + odd = use_par ? odd : 0; + while (i2 - i1 > 1) + { + CGU_UINT8 j = (CGU_UINT8)((i1 + i2) * 0.5f); + CGV_FLOAT ep_d = (CGV_FLOAT)cmp_expandbits((j << use_par) + odd, bits); + if (v >= ep_d) + i1 = j; + else + i2 = j; + } + + return (i1 << use_par) + odd; +} + +CMP_STATIC CGV_FLOAT cmp_absf(CMP_IN CGV_FLOAT a) +{ + return a > 0.0F ? a : -a; +} + +CMP_STATIC INLINE CGU_UINT32 cmp_pow2Packed(CMP_IN CGU_INT x) +{ + return 1 << x; +} + +CMP_STATIC INLINE CGU_UINT8 cmp_clampIndex(CMP_IN CGU_UINT8 v, CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + +CMP_STATIC INLINE CGU_UINT8 shift_right_uint82(CMP_IN CGU_UINT8 v, CMP_IN CGU_UINT8 bits) +{ + return v >> bits; // (perf warning expected) +} + +#endif + +CMP_STATIC CGU_INT cmp_QuantizeToBitSize(CMP_IN CGU_INT value, CMP_IN CGU_INT prec, CMP_IN CGU_BOOL signedfloat16) +{ + if (prec <= 1) + return 0; + CGU_BOOL negvalue = false; + + // move data to use extra bits for processing + CGU_INT ivalue = value; + + if (signedfloat16) + { + if (value < 0) + { + negvalue = true; + value = -value; + } + prec--; + } + else + { + // clamp -ve + if (value < 0) + value = 0; + } + + CGU_INT iQuantized; + CGU_INT bias = (prec > 10 && prec != 16) ? ((1 << (prec - 11)) - 1) : 0; + bias = (prec == 16) ? 15 : bias; + + iQuantized = ((ivalue << prec) + bias) / (0x7bff + 1); // 16 bit Float Max 0x7bff + + return (negvalue ? -iQuantized : iQuantized); +} + +//======================================================= +// CPU GPU Macro API +//======================================================= + +#ifdef ASPM_GPU +#define cmp_min(a, b) min(a, b) +#define cmp_max(a, b) max(a, b) +#else +#ifndef cmp_min +#define cmp_min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef cmp_max +#define cmp_max(a, b) ((a) > (b) ? (a) : (b)) +#endif +#endif + +//======================================================= +// CPU Template API +//======================================================= + +#ifndef ASPM_GPU +#ifndef TEMPLATE_API_INTERFACED +#define TEMPLATE_API_INTERFACED + +template +T clamp(T& v, const T& lo, const T& hi) +{ + if (v < lo) + return lo; + else if (v > hi) + return hi; + return v; +} + +template +Vec4T clamp(Vec4T& v, const T& lo, const T& hi) +{ + Vec4T res = v; + if (v.x < lo) + res.x = lo; + else if (v.x > hi) + res.x = hi; + if (v.y < lo) + res.y = lo; + else if (v.y > hi) + res.y = hi; + if (v.z < lo) + res.z = lo; + else if (v.w > hi) + res.w = hi; + if (v.w < lo) + res.w = lo; + else if (v.z > hi) + res.z = hi; + return res; +} + + +template +T dot(T& v1, T2& v2) +{ + return (v1 * v2); +} + +template +T dot(Vec4T& v1, Vec4T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w); +} + +template +T dot(Vec4T& v1, Vec4T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w); +} +template +T dot(Vec3T& v1, Vec3T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z); +} + +template +T dot(Vec3T& v1, Vec3T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z); +} + +#endif // API_INTERFACED +#endif // ASPM_GPU + +#endif // diff --git a/WickedEngine/shaders/compressonator/bcn_common_kernel.h b/WickedEngine/shaders/compressonator/bcn_common_kernel.h new file mode 100644 index 000000000..e6ca1dea9 --- /dev/null +++ b/WickedEngine/shaders/compressonator/bcn_common_kernel.h @@ -0,0 +1,2956 @@ +//============================================================================= +// Copyright (c) 2018-2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//===================================================================== +//===================================================================== +// Block-compression (BC) functionality ref +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//===================================================================== +//************************************************************************************ +// ** NOTE ** +// Content and data types may change, use CMP_Core.h for interface to your application +//************************************************************************************ + +#ifndef _BCN_COMMON_KERNEL_H +#define _BCN_COMMON_KERNEL_H + +#pragma warning(disable : 4505) // disable warnings on unreferenced local function has been removed + +#include "common_def.h" +#include "bcn_common_api.h" + +//----------------------------------------------------------------------- +// When build is for CPU, we have some missing API calls common to GPU +// Use CPU CMP_Core replacements +//----------------------------------------------------------------------- +// used in BC1 HiQuaity +#if defined(ASPM_GPU) || defined(ASPM_HLSL) || defined(ASPM_OPENCL) +#define ALIGN_16 +#else +#include INC_cmp_math_func +#if defined(WIN32) || defined(_WIN64) +#define ALIGN_16 __declspec(align(16)) +#else // !WIN32 && !_WIN64 +#define ALIGN_16 +#endif // !WIN32 && !_WIN64 +#endif + + + +#define DXTC_OFFSET_ALPHA 0 +#define DXTC_OFFSET_RGB 2 + +#define BC1CompBlockSize 8 + +#define RC 2 +#define GC 1 +#define BC 0 +#define AC 3 + +/* +Channel Bits +*/ +#define RGBA8888_CHANNEL_A 3 +#define RGBA8888_CHANNEL_R 2 +#define RGBA8888_CHANNEL_G 1 +#define RGBA8888_CHANNEL_B 0 +#define RGBA8888_OFFSET_A (RGBA8888_CHANNEL_A * 8) +#define RGBA8888_OFFSET_R (RGBA8888_CHANNEL_R * 8) +#define RGBA8888_OFFSET_G (RGBA8888_CHANNEL_G * 8) +#define RGBA8888_OFFSET_B (RGBA8888_CHANNEL_B * 8) + +#ifndef MAX_ERROR +#define MAX_ERROR 128000.f +#endif + +#define MAX_BLOCK 64 +#define MAX_POINTS 16 +#define BLOCK_SIZE MAX_BLOCK +#define NUM_CHANNELS 4 +#define NUM_ENDPOINTS 2 +#define BLOCK_SIZE_4X4 16 +#define CMP_ALPHA_RAMP 8 // Number of Ramp Points used for Alpha Channels in BC5 + +#define ConstructColour(r, g, b) (((r) << 11) | ((g) << 5) | (b)) // same as BC1ConstructColour in common_api + +#define BYTEPP 4 +#define CMP_QUALITY0 0.25f +#define CMP_QUALITY1 0.50f +#define CMP_QUALITY2 0.75f +#define POS(x, y) (pos_on_axis[(x) + (y)*4]) + +// Find the first approximation of the line +// Assume there is a linear relation +// Z = a * X_In +// Z = b * Y_In +// Find a,b to minimize MSE between Z and Z_In +#define EPS (2.f / 255.f) * (2.f / 255.f) +#define EPS2 3.f * (2.f / 255.f) * (2.f / 255.f) + +// Grid precision +#define PIX_GRID 8 + +#define BYTE_MASK 0x00ff + +#define SCH_STPS 3 // number of search steps to make at each end of interval +static CMP_CONSTANT CGU_FLOAT sMvF[] = {0.f, -1.f, 1.f, -2.f, 2.f, -3.f, 3.f, -4.f, 4.f, -5.f, 5.f, -6.f, 6.f, -7.f, 7.f, -8.f, 8.f}; + +#ifndef GBL_SCH_STEP +#define GBL_SCH_STEP_MXS 0.018f +#define GBL_SCH_EXT_MXS 0.1f +#define LCL_SCH_STEP_MXS 0.6f +#define GBL_SCH_STEP_MXQ 0.0175f +#define GBL_SCH_EXT_MXQ 0.154f +#define LCL_SCH_STEP_MXQ 0.45f + +#define GBL_SCH_STEP GBL_SCH_STEP_MXS +#define GBL_SCH_EXT GBL_SCH_EXT_MXS +#define LCL_SCH_STEP LCL_SCH_STEP_MXS +#endif + +#ifndef ASPM_GPU + +typedef union +{ + struct colorblock64U + { + CGU_UINT8 col0; + CGU_UINT8 col1; + CGU_UINT8 indices[6]; + }; + CGU_INT8 cmp_data8[8]; + CGU_INT32 cmp_data32[2]; + CGU_UINT64 cmp_data64; +} CMP_BLOCK64_UNORM; + +typedef union +{ + struct colorblock64S + { + CGU_UINT8 col0; + CGU_UINT8 col1; + CGU_UINT8 indices[6]; + }; + CGU_INT8 cmp_data8[8]; + CGU_INT32 cmp_data32[2]; + CGU_UINT64 cmp_data64[2]; +} CMP_BLOCK64_SNORM; + +typedef union +{ + CGU_INT8 cmp_data8[16]; + CGU_INT32 cmp_data32[4]; + CGU_UINT64 cmp_data64[2]; +} CMP_BLOCK128_UNORM; + +#endif + +typedef struct +{ + CGU_UINT32 data; + CGU_UINT32 index; +} CMP_di; + +typedef struct +{ + CGU_FLOAT data; + CGU_UINT32 index; +} CMP_df; + +typedef struct +{ + // user setable + CGU_FLOAT m_fquality; + CGU_FLOAT m_fChannelWeights[3]; + CGU_BOOL m_bUseChannelWeighting; + CGU_BOOL m_bUseAdaptiveWeighting; + CGU_BOOL m_bUseFloat; + CGU_BOOL m_b3DRefinement; + CGU_BOOL m_bUseAlpha; + CGU_BOOL m_bIsSRGB; // Use Linear to SRGB color conversion used in BC1, default is false + CGU_BOOL m_bIsSNORM; + CGU_BOOL m_sintsrc; // source data pointer is signed data + CGU_UINT32 m_nRefinementSteps; + CGU_UINT32 m_nAlphaThreshold; + CGU_BOOL m_mapDecodeRGBA; + CGU_UINT32 m_src_width; + CGU_UINT32 m_src_height; +} CMP_BC15Options; + +typedef struct +{ + CGU_Vec3i end_point0; + CGU_Vec3i end_point1; + CGU_UINT8 indices[16]; + CGU_BOOL m_3color; +} CMP_BC1_Encode_Results; + +// used in BC1 LowQuality code +typedef struct +{ + CGU_Vec3f Color0; + CGU_Vec3f Color1; +} CMP_EndPoints; + +// Common data info used between encoders +// Defines properties of current 4x4 pixel block +typedef struct +{ + CGU_UINT32 grayscale_flag; + CGU_UINT32 any_black_pixels; + CGU_BOOL all_colors_equal; + CGU_Vec3i min; + CGU_Vec3i max; + CGU_Vec3i total; + CGU_Vec3i avg; +} CMP_EncodeData; + +typedef struct CMP_BC1_Block_t +{ + // Union struct not supported on GPU + // 8 Bytes Total + #ifndef ASPM_GPU + union { + struct { // 2 x 32bit + CGU_UINT32 colors; + CGU_UINT32 indices; + }; + struct { // 8 x 8bit + CGU_UINT8 m_low_color[2]; + CGU_UINT8 m_high_color[2]; + CGU_UINT8 m_selectors[4]; + }; + }; + + inline void set_low_color(CGU_UINT16 c) + { + m_low_color[0] = static_cast(c & 0xFF); + m_low_color[1] = static_cast((c >> 8) & 0xFF); + } + inline void set_high_color(CGU_UINT16 c) + { + m_high_color[0] = static_cast(c & 0xFF); + m_high_color[1] = static_cast((c >> 8) & 0xFF); + } + #else + CGU_UINT32 colors; + CGU_UINT32 indices; + #endif +} CMP_BC1_Block; + +// Helper functions to cut precision of floats +// Prec is a power of 10 value from 1,10,100,...,10000... INT MAX power 10 +static CGU_BOOL cmp_compareprecision(CGU_FLOAT f1, CGU_FLOAT f2, CGU_INT Prec) +{ + CGU_INT scale1 = (CGU_INT)(f1 * Prec); + CGU_INT scale2 = (CGU_INT)(f2 * Prec); + return (scale1 == scale2); +} + +// Helper function to compare floats to a set precision +static CGU_FLOAT cmp_getfloatprecision(CGU_FLOAT f1, CGU_INT Prec) +{ + CGU_INT scale1 = (CGU_INT)(f1 * Prec); + return ((CGU_FLOAT)(scale1) / Prec); +} + +static CGU_FLOAT cmp_getIndicesRGB(CMP_INOUT CGU_UINT32 CMP_PTRINOUT cmpindex, + const CGU_Vec3f block[16], + CGU_Vec3f minColor, + CGU_Vec3f maxColor, + CGU_BOOL getErr) +{ + CGU_UINT32 PackedIndices = 0; + CGU_FLOAT err = 0.0f; + CGU_Vec3f cn[4]; + CGU_FLOAT minDistance; + + if (getErr) + { + // remap to BC1 spec for decoding offsets, + // where cn[0] > cn[1] Max Color = index 0, 2/3 offset =index 2, 1/3 offset = index 3, Min Color = index 1 + cn[0] = maxColor; + cn[1] = minColor; + cn[2] = cn[0] * 2.0f / 3.0f + cn[1] * 1.0f / 3.0f; + cn[3] = cn[0] * 1.0f / 3.0f + cn[1] * 2.0f / 3.0f; + } + + CGU_FLOAT Scale = 3.f / dot(minColor - maxColor, minColor - maxColor); + CGU_Vec3f ScaledRange = (minColor - maxColor) * Scale; + CGU_FLOAT Bias = (dot(maxColor, maxColor) - dot(maxColor, minColor)) * Scale; + CGU_INT indexMap[4] = {0, 2, 3, 1}; // mapping based on BC1 Spec for color0 > color1 + CGU_UINT32 index; + CGU_FLOAT diff; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + // Get offset from base scale + diff = dot(block[i], ScaledRange) + Bias; + index = ((CGU_UINT32)round(diff)) & 0x3; + + // remap linear offset to spec offset + index = indexMap[index]; + + // use err calc for use in higher quality code + if (getErr) + { + minDistance = dot(block[i] - cn[index], block[i] - cn[index]); + err += minDistance; + } + + // Map the 2 bit index into compress 32 bit block + if (index) + PackedIndices |= (index << (2 * i)); + } + + if (getErr) + err = err * 0.0208333f; + + CMP_PTRINOUT cmpindex = PackedIndices; + return err; +} + +//---------------------------------------- BCn Common Utility Code ------------------------------------------------------- + +#ifndef ASPM_GPU +static void SetDefaultBC15Options(CMP_BC15Options* BC15Options) +{ + if (BC15Options) + { + BC15Options->m_fquality = 1.0f; + BC15Options->m_bUseChannelWeighting = false; + BC15Options->m_bUseAdaptiveWeighting = false; + BC15Options->m_fChannelWeights[0] = 0.3086f; + BC15Options->m_fChannelWeights[1] = 0.6094f; + BC15Options->m_fChannelWeights[2] = 0.0820f; + BC15Options->m_nAlphaThreshold = 128; + BC15Options->m_bUseFloat = false; + BC15Options->m_b3DRefinement = false; + BC15Options->m_bUseAlpha = false; + BC15Options->m_bIsSNORM = false; + BC15Options->m_bIsSRGB = false; + BC15Options->m_nRefinementSteps = 0; + BC15Options->m_src_width = 4; + BC15Options->m_src_height = 4; +#ifdef CMP_SET_BC13_DECODER_RGBA + BC15Options->m_mapDecodeRGBA = true; +#else + BC15Options->m_mapDecodeRGBA = false; +#endif + } +} +#endif + +static CMP_BC15Options CalculateColourWeightings(CGU_Vec4f rgbaBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) +{ + CGU_FLOAT fBaseChannelWeights[3] = {0.3086f, 0.6094f, 0.0820f}; + + if (!BC15options.m_bUseChannelWeighting) + { + BC15options.m_fChannelWeights[0] = 1.0F; + BC15options.m_fChannelWeights[1] = 1.0F; + BC15options.m_fChannelWeights[2] = 1.0F; + return BC15options; + } + + if (BC15options.m_bUseAdaptiveWeighting) + { + float medianR = 0.0f, medianG = 0.0f, medianB = 0.0f; + + for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) + { + medianR += rgbaBlock[k].x; + medianG += rgbaBlock[k].y; + medianB += rgbaBlock[k].z; + } + + medianR /= BLOCK_SIZE_4X4; + medianG /= BLOCK_SIZE_4X4; + medianB /= BLOCK_SIZE_4X4; + + // Now skew the colour weightings based on the gravity center of the block + float largest = max(max(medianR, medianG), medianB); + + if (largest > 0) + { + medianR /= largest; + medianG /= largest; + medianB /= largest; + } + else + medianR = medianG = medianB = 1.0f; + + // Scale weightings back up to 1.0f + CGU_FLOAT fWeightScale = 1.0f / (fBaseChannelWeights[0] + fBaseChannelWeights[1] + fBaseChannelWeights[2]); + + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0] * fWeightScale; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1] * fWeightScale; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2] * fWeightScale; + + BC15options.m_fChannelWeights[0] = ((BC15options.m_fChannelWeights[0] * 3 * medianR) + BC15options.m_fChannelWeights[0]) * 0.25f; + BC15options.m_fChannelWeights[1] = ((BC15options.m_fChannelWeights[1] * 3 * medianG) + BC15options.m_fChannelWeights[1]) * 0.25f; + BC15options.m_fChannelWeights[2] = ((BC15options.m_fChannelWeights[2] * 3 * medianB) + BC15options.m_fChannelWeights[2]) * 0.25f; + + fWeightScale = 1.0f / (BC15options.m_fChannelWeights[0] + BC15options.m_fChannelWeights[1] + BC15options.m_fChannelWeights[2]); + + BC15options.m_fChannelWeights[0] *= fWeightScale; + BC15options.m_fChannelWeights[1] *= fWeightScale; + BC15options.m_fChannelWeights[2] *= fWeightScale; + } + else + { + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0]; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1]; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2]; + } + + return BC15options; +} + +static CMP_BC15Options CalculateColourWeightings3f(CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) +{ + CGU_FLOAT fBaseChannelWeights[3] = {0.3086f, 0.6094f, 0.0820f}; + + if (!BC15options.m_bUseChannelWeighting) + { + BC15options.m_fChannelWeights[0] = 1.0F; + BC15options.m_fChannelWeights[1] = 1.0F; + BC15options.m_fChannelWeights[2] = 1.0F; + return BC15options; + } + + if (BC15options.m_bUseAdaptiveWeighting) + { + float medianR = 0.0f, medianG = 0.0f, medianB = 0.0f; + + for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) + { + medianR += rgbBlock[k].x; + medianG += rgbBlock[k].y; + medianB += rgbBlock[k].z; + } + + medianR /= BLOCK_SIZE_4X4; + medianG /= BLOCK_SIZE_4X4; + medianB /= BLOCK_SIZE_4X4; + + // Now skew the colour weightings based on the gravity center of the block + float largest = max(max(medianR, medianG), medianB); + + if (largest > 0) + { + medianR /= largest; + medianG /= largest; + medianB /= largest; + } + else + medianR = medianG = medianB = 1.0f; + + // Scale weightings back up to 1.0f + CGU_FLOAT fWeightScale = 1.0f / (fBaseChannelWeights[0] + fBaseChannelWeights[1] + fBaseChannelWeights[2]); + + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0] * fWeightScale; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1] * fWeightScale; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2] * fWeightScale; + + BC15options.m_fChannelWeights[0] = ((BC15options.m_fChannelWeights[0] * 3 * medianR) + BC15options.m_fChannelWeights[0]) * 0.25f; + BC15options.m_fChannelWeights[1] = ((BC15options.m_fChannelWeights[1] * 3 * medianG) + BC15options.m_fChannelWeights[1]) * 0.25f; + BC15options.m_fChannelWeights[2] = ((BC15options.m_fChannelWeights[2] * 3 * medianB) + BC15options.m_fChannelWeights[2]) * 0.25f; + + fWeightScale = 1.0f / (BC15options.m_fChannelWeights[0] + BC15options.m_fChannelWeights[1] + BC15options.m_fChannelWeights[2]); + + BC15options.m_fChannelWeights[0] *= fWeightScale; + BC15options.m_fChannelWeights[1] *= fWeightScale; + BC15options.m_fChannelWeights[2] *= fWeightScale; + } + else + { + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0]; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1]; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2]; + } + + return BC15options; +} + +static CGU_FLOAT cmp_getRampErr(CGU_FLOAT Prj[BLOCK_SIZE_4X4], + CGU_FLOAT PrjErr[BLOCK_SIZE_4X4], + CGU_FLOAT PreMRep[BLOCK_SIZE_4X4], + CGU_FLOAT StepErr, + CGU_FLOAT lowPosStep, + CGU_FLOAT highPosStep, + CGU_UINT32 dwUniqueColors) +{ + CGU_FLOAT error = 0; + CGU_FLOAT step = (highPosStep - lowPosStep) / 3; // using (dwNumChannels=4 - 1); + CGU_FLOAT step_h = step * (CGU_FLOAT)0.5; + CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step; + + for (CGU_UINT32 i = 0; i < dwUniqueColors; i++) + { + CGU_FLOAT v; + // Work out which value in the block this select + CGU_FLOAT del; + + if ((del = Prj[i] - lowPosStep) <= 0) + v = lowPosStep; + else if (Prj[i] - highPosStep >= 0) + v = highPosStep; + else + v = floor((del + step_h) * rstep) * step + lowPosStep; + + // And accumulate the error + CGU_FLOAT d = (Prj[i] - v); + d *= d; + CGU_FLOAT err = PreMRep[i] * d + PrjErr[i]; + error += err; + if (StepErr < error) + { + error = StepErr; + break; + } + } + return error; +} + +static CGU_Vec2ui cmp_compressExplicitAlphaBlock(const CGU_FLOAT AlphaBlockUV[16]) +{ + CGU_Vec2ui compBlock = {0, 0}; + CGU_UINT8 i; + for (i = 0; i < 16; i++) + { + CGU_UINT8 v = (CGU_UINT8)(AlphaBlockUV[i] * 255.0F); + v = (v + 7 - (v >> 4)); + v >>= 4; + + if (v < 0) + v = 0; + else if (v > 0xf) + v = 0xf; + + if (i < 8) + compBlock.x |= v << (4 * i); + else + compBlock.y |= v << (4 * (i - 8)); + } + return compBlock; +} + +static CGU_FLOAT cmp_getRampError(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], + CGU_FLOAT _Rpt[BLOCK_SIZE_4X4], + CGU_FLOAT _maxerror, + CGU_FLOAT _min_ex, + CGU_FLOAT _max_ex, + CGU_INT _NmbrClrs) +{ // Max 16 + CGU_INT i; + CGU_FLOAT error = 0; + const CGU_FLOAT step = (_max_ex - _min_ex) / 7; // (CGU_FLOAT)(dwNumPoints - 1); + const CGU_FLOAT step_h = step * 0.5f; + const CGU_FLOAT rstep = 1.0f / step; + + for (i = 0; i < _NmbrClrs; i++) + { + CGU_FLOAT v; + // Work out which value in the block this select + CGU_FLOAT del; + + if ((del = _Blk[i] - _min_ex) <= 0) + v = _min_ex; + else if (_Blk[i] - _max_ex >= 0) + v = _max_ex; + else + v = (floor((del + step_h) * rstep) * step) + _min_ex; + + // And accumulate the error + CGU_FLOAT del2 = (_Blk[i] - v); + error += del2 * del2 * _Rpt[i]; + + // if we've already lost to the previous step bail out + if (_maxerror < error) + { + error = _maxerror; + break; + } + } + return error; +} + +static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], + CGU_FLOAT _Rpt[BLOCK_SIZE_4X4], + CGU_FLOAT _MaxError, + CMP_INOUT CGU_FLOAT CMP_PTRINOUT _min_ex, + CMP_INOUT CGU_FLOAT CMP_PTRINOUT _max_ex, + CGU_FLOAT _m_step, + CGU_FLOAT _min_bnd, + CGU_FLOAT _max_bnd, + CGU_INT _NmbrClrs) +{ + // Start out assuming our endpoints are the min and max values we've + // determined + + // Attempt a (simple) progressive refinement step to reduce noise in the + // output image by trying to find a better overall match for the endpoints. + + CGU_FLOAT maxerror = _MaxError; + CGU_FLOAT min_ex = CMP_PTRINOUT _min_ex; + CGU_FLOAT max_ex = CMP_PTRINOUT _max_ex; + + CGU_INT mode, bestmode; + + do + { + CGU_FLOAT cr_min0 = min_ex; + CGU_FLOAT cr_max0 = max_ex; + for (bestmode = -1, mode = 0; mode < SCH_STPS * SCH_STPS; mode++) + { + // check each move (see sStep for direction) + CGU_FLOAT cr_min = min_ex + _m_step * sMvF[mode / SCH_STPS]; + CGU_FLOAT cr_max = max_ex + _m_step * sMvF[mode % SCH_STPS]; + + cr_min = max(cr_min, _min_bnd); + cr_max = min(cr_max, _max_bnd); + + CGU_FLOAT error; + error = cmp_getRampError(_Blk, _Rpt, maxerror, cr_min, cr_max, _NmbrClrs); + + if (error < maxerror) + { + maxerror = error; + bestmode = mode; + cr_min0 = cr_min; + cr_max0 = cr_max; + } + } + + if (bestmode != -1) + { + // make move (see sStep for direction) + min_ex = cr_min0; + max_ex = cr_max0; + } + } while (bestmode != -1); + + CMP_PTRINOUT _min_ex = min_ex; + CMP_PTRINOUT _max_ex = max_ex; + + return maxerror; +} + +static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) +{ + CGU_UINT32 i; + CGU_Vec2f cmpMinMax; + + //================================================================ + // Bounding Box + // lowest quality calculation to get min and max value to use + //================================================================ + if (fquality < CMP_QUALITY2) + { + cmpMinMax.x = _Blk[0]; + cmpMinMax.y = _Blk[0]; + for (i = 1; i < BLOCK_SIZE_4X4; ++i) + { + cmpMinMax.x = min(cmpMinMax.x, _Blk[i]); + cmpMinMax.y = max(cmpMinMax.y, _Blk[i]); + } + return cmpMinMax; + } + + //================================================================ + // Do more calculations to get the best min and max values to use + //================================================================ + CGU_FLOAT Ramp[2]; + + // Result defaults for SNORM or UNORM + Ramp[0] = isSigned ? -1.0f : 0.0f; + Ramp[1] = 1.0f; + + ALIGN_16 CGU_FLOAT afUniqueValues[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT afValueRepeats[BLOCK_SIZE_4X4]; + for (i = 0; i < BLOCK_SIZE_4X4; i++) + afUniqueValues[i] = afValueRepeats[i] = 0.f; + + // For each unique value we compute the number of it appearances. + CGU_FLOAT fBlk[BLOCK_SIZE_4X4]; + +// sort the input +#ifndef ASPM_GPU + memcpy(fBlk, _Blk, BLOCK_SIZE_4X4 * sizeof(CGU_FLOAT)); + qsort((void*)fBlk, (size_t)BLOCK_SIZE_4X4, sizeof(CGU_FLOAT), QSortFCmp); +#else + CGU_UINT32 j; + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + fBlk[i] = _Blk[i]; + } + + CMP_df what[BLOCK_SIZE]; + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + what[i].index = i; + what[i].data = fBlk[i]; + } + + CGU_UINT32 tmp_index; + CGU_FLOAT tmp_data; + + for (i = 1; i < BLOCK_SIZE_4X4; i++) + { + for (j = i; j > 0; j--) + { + if (what[j - 1].data > what[j].data) + { + tmp_index = what[j].index; + tmp_data = what[j].data; + what[j].index = what[j - 1].index; + what[j].data = what[j - 1].data; + what[j - 1].index = tmp_index; + what[j - 1].data = tmp_data; + } + } + } + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + fBlk[i] = what[i].data; +#endif + + CGU_FLOAT new_p = -2.0f; + + CGU_UINT32 dwUniqueValues = 0; + afUniqueValues[0] = 0.0f; + CGU_BOOL requiresCalculation = true; + + { + // Ramp not fixed + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + if (new_p != fBlk[i]) + { + afUniqueValues[dwUniqueValues] = new_p = fBlk[i]; + afValueRepeats[dwUniqueValues] = 1.f; + dwUniqueValues++; + } + else if (dwUniqueValues) + afValueRepeats[dwUniqueValues - 1] += 1.f; + } + + // if number of unique colors is less or eq 2, we've done + if (dwUniqueValues <= 2) + { + Ramp[0] = floor(afUniqueValues[0] * 255.0f + 0.5f); + if (dwUniqueValues == 1) + Ramp[1] = Ramp[0] + 1.f; + else + Ramp[1] = floor(afUniqueValues[1] * 255.0f + 0.5f); + requiresCalculation = false; + } + } // Ramp not fixed + + if (requiresCalculation) + { + CGU_FLOAT min_ex = afUniqueValues[0]; + CGU_FLOAT max_ex = afUniqueValues[dwUniqueValues - 1]; + CGU_FLOAT min_bnd = 0, max_bnd = 1.; + CGU_FLOAT min_r = min_ex, max_r = max_ex; + CGU_FLOAT gbl_l = 0, gbl_r = 0; + CGU_FLOAT cntr = (min_r + max_r) / 2; + + CGU_FLOAT gbl_err = MAX_ERROR; + // Trying to avoid unnecessary calculations. Heuristics: after some analisis + // it appears that in integer case, if the input interval not more then 48 + // we won't get much better + bool wantsSearch = !((max_ex - min_ex) <= (48.f / 256.0f)); + + if (wantsSearch) + { + // Search. + // 1. take the vicinities of both low and high bound of the input + // interval. + // 2. setup some search step + // 3. find the new low and high bound which provides an (sub) optimal + // (infinite precision) clusterization. + CGU_FLOAT gbl_llb = (min_bnd > min_r - GBL_SCH_EXT) ? min_bnd : min_r - GBL_SCH_EXT; + CGU_FLOAT gbl_rrb = (max_bnd < max_r + GBL_SCH_EXT) ? max_bnd : max_r + GBL_SCH_EXT; + CGU_FLOAT gbl_lrb = (cntr < min_r + GBL_SCH_EXT) ? cntr : min_r + GBL_SCH_EXT; + CGU_FLOAT gbl_rlb = (cntr > max_r - GBL_SCH_EXT) ? cntr : max_r - GBL_SCH_EXT; + + for (CGU_FLOAT step_l = gbl_llb; step_l < gbl_lrb; step_l += GBL_SCH_STEP) + { + for (CGU_FLOAT step_r = gbl_rrb; gbl_rlb <= step_r; step_r -= GBL_SCH_STEP) + { + CGU_FLOAT sch_err; + // an sse version is avaiable + sch_err = cmp_getRampError(afUniqueValues, afValueRepeats, gbl_err, step_l, step_r, dwUniqueValues); + if (sch_err < gbl_err) + { + gbl_err = sch_err; + gbl_l = step_l; + gbl_r = step_r; + } + } + } + + min_r = gbl_l; + max_r = gbl_r; + } // want search + + // This is a refinement call. The function tries to make several small + // stretches or squashes to minimize quantization error. + CGU_FLOAT m_step = LCL_SCH_STEP / 256.0f; + cmp_linearBlockRefine(afUniqueValues, afValueRepeats, gbl_err, CMP_REFINOUT min_r, CMP_REFINOUT max_r, m_step, min_bnd, max_bnd, dwUniqueValues); + + min_ex = min_r; + max_ex = max_r; + max_ex *= 255.0f; + min_ex *= 255.0f; + + Ramp[0] = floor(min_ex + 0.5f); + Ramp[1] = floor(max_ex + 0.5f); + } + + // Ensure that the two endpoints are not the same + // This is legal but serves no need & can break some optimizations in the compressor + if (Ramp[0] == Ramp[1]) + { + if (Ramp[1] < 255.f) + Ramp[1] = Ramp[1] + 1.0f; + else if (Ramp[1] > 0.0f) + Ramp[1] = Ramp[1] - 1.0f; + } + + cmpMinMax.x = Ramp[0]; + cmpMinMax.y = Ramp[1]; + + return cmpMinMax; +} + +static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality) +{ + CGU_UINT32 i; + CGU_UINT32 j; + CGU_Vec2ui cmpBlock = {0, 0}; + CGU_UINT32 MinRampU; + CGU_UINT32 MaxRampU; + CGU_INT32 pcIndices[BLOCK_SIZE_4X4]; + + if (fquality < CMP_QUALITY2) + { + CGU_FLOAT Range; + CGU_FLOAT RampSteps; // segments into 0..7 sections + CGU_FLOAT Bias; + + if (RampMinMax.x != RampMinMax.y) + Range = RampMinMax.x - RampMinMax.y; + else + Range = 1.0f; + + RampSteps = 7.f / Range; // segments into 0..7 sections + Bias = -RampSteps * RampMinMax.y; + + for (i = 0; i < 16; ++i) + { + pcIndices[i] = (CGU_UINT32)round(alphaBlock[i] * RampSteps + Bias); + if (i < 5) + { + pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7)); + } + else if (i > 5) + { + pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7 ? 1 : 0)); + } + else + { + pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7)); + } + } + + MinRampU = (CGU_UINT32)round(RampMinMax.x * 255.0f); + MaxRampU = (CGU_UINT32)round(RampMinMax.y * 255.0f); + + cmpBlock.x = (MinRampU << 8) | MaxRampU; + cmpBlock.y = 0; + for (i = 0; i < 5; ++i) + { + cmpBlock.x |= (pcIndices[i] << (16 + (i * 3))); + } + { + cmpBlock.x |= (pcIndices[5] << 31); + cmpBlock.y |= (pcIndices[5] >> 1); + } + for (i = 6; i < BLOCK_SIZE_4X4; ++i) + { + cmpBlock.y |= (pcIndices[i] << (i * 3 - 16)); + } + } + else + { + CGU_UINT32 epoint; + CGU_FLOAT alpha[BLOCK_SIZE_4X4]; + CGU_FLOAT OverIntFctr; + CGU_FLOAT shortest; + CGU_FLOAT adist; + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + pcIndices[i] = 0; + + for (i = 0; i < MAX_POINTS; i++) + alpha[i] = 0; + + // GetRmp1 + { + if (RampMinMax.x <= RampMinMax.y) + { + CGU_FLOAT t = RampMinMax.x; + RampMinMax.x = RampMinMax.y; + RampMinMax.y = t; + } + + //============================= + // final clusterization applied + //============================= + CGU_FLOAT ramp[NUM_ENDPOINTS]; + + ramp[0] = RampMinMax.x; + ramp[1] = RampMinMax.y; + + { + // BldRmp1 + alpha[0] = ramp[0]; + alpha[1] = ramp[1]; + for (epoint = 1; epoint < CMP_ALPHA_RAMP - 1; epoint++) + alpha[epoint + 1] = (alpha[0] * (CMP_ALPHA_RAMP - 1 - epoint) + alpha[1] * epoint) / (CGU_FLOAT)(CMP_ALPHA_RAMP - 1); + for (epoint = CMP_ALPHA_RAMP; epoint < BLOCK_SIZE_4X4; epoint++) + alpha[epoint] = 100000.f; + } // BldRmp1 + + // FixedRamp + for (i = 0; i < CMP_ALPHA_RAMP; i++) + { + alpha[i] = floor(alpha[i] + 0.5f); + } + } // GetRmp1 + + OverIntFctr = 1.f / 255.0f; + for (i = 0; i < CMP_ALPHA_RAMP; i++) + alpha[i] *= OverIntFctr; + + // For each colour in the original block, calculate its weighted + // distance from each point in the original and assign it + // to the closest cluster + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + shortest = 10000000.f; + for (j = 0; j < CMP_ALPHA_RAMP; j++) + { + adist = (alphaBlock[i] - alpha[j]); + adist *= adist; + if (adist < shortest) + { + shortest = adist; + pcIndices[i] = j; + } + } + } + + //================================================== + // EncodeAlphaBlock + //================================================== + MinRampU = (CGU_UINT32)RampMinMax.x; + MaxRampU = (CGU_UINT32)RampMinMax.y; + + cmpBlock.x = (MaxRampU << 8) | MinRampU; + cmpBlock.y = 0; + for (i = 0; i < 5; i++) + { + cmpBlock.x |= (pcIndices[i]) << (16 + (i * 3)); + } + { + cmpBlock.x |= (pcIndices[5] & 0x1) << 31; + cmpBlock.y |= (pcIndices[5] & 0x6) >> 1; + } + for (i = 6; i < BLOCK_SIZE_4X4; i++) + { + cmpBlock.y |= (pcIndices[i]) << (i * 3 - 16); + } + } + return cmpBlock; +} + +//======================= SNORM CODE ================================== + +static CGU_INT8 cmp_snormFloatToSInt(CGU_FLOAT fsnorm) +{ + if (isnan(fsnorm)) + fsnorm = 0; + else if (fsnorm > 1) + fsnorm = 1; // Clamp to 1 + else if (fsnorm < -1) + fsnorm = -1; // Clamp to -1 + + fsnorm = fsnorm * 127U; + + // shift round up or down + if (fsnorm >= 0) + fsnorm += .5f; + else + fsnorm -= .5f; + +#ifdef ASPM_GPU + CGU_INT8 res = (CGU_INT8)fsnorm; +#else + CGU_INT8 res = static_cast(fsnorm); +#endif + return (res); +} + +static CGU_Vec2f cmp_optimizeEndPoints(CGU_FLOAT pPoints[BLOCK_SIZE_4X4], CGU_INT8 cSteps, CGU_BOOL isSigned) +{ + CGU_Vec2f fendpoints; + CGU_FLOAT MAX_VALUE = 1.0f; + CGU_FLOAT MIN_VALUE = isSigned ? -1.0f : 0.0f; + + // Find Min and Max points, as starting point + CGU_FLOAT fX = MAX_VALUE; + CGU_FLOAT fY = MIN_VALUE; + + if (8 == cSteps) + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY) + fY = pPoints[iPoint]; + } + } + else + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX && pPoints[iPoint] > MIN_VALUE) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY && pPoints[iPoint] < MAX_VALUE) + fY = pPoints[iPoint]; + } + + if (fX == fY) + { + fY = MAX_VALUE; + } + } + + //=================== + // Use Newton Method + //=================== +#ifdef ASPM_GPU + CGU_FLOAT cStepsDiv = (CGU_FLOAT)(cSteps - 1); +#else + CGU_FLOAT cStepsDiv = static_cast(cSteps - 1); +#endif + CGU_FLOAT pSteps[8]; + CGU_FLOAT fc; + CGU_FLOAT fd; + + for (CGU_INT8 iIteration = 0; iIteration < 8; iIteration++) + { + // reach minimum threashold break + if ((fY - fX) < (1.0f / 256.0f)) + break; + + CGU_FLOAT fScale = cStepsDiv / (fY - fX); + + // Calculate new steps + for (CGU_INT8 iStep = 0; iStep < cSteps; iStep++) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + pSteps[iStep] = fc * fX + fd * fY; + } + + if (6 == cSteps) + { + pSteps[6] = MIN_VALUE; + pSteps[7] = MAX_VALUE; + } + + // Evaluate function, and derivatives + CGU_FLOAT dX = 0.0f; + CGU_FLOAT dY = 0.0f; + CGU_FLOAT d2X = 0.0f; + CGU_FLOAT d2Y = 0.0f; + + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + CGU_FLOAT fDot = (pPoints[iPoint] - fX) * fScale; + + CGU_INT8 iStep; + if (fDot <= 0.0f) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] <= (fX + MIN_VALUE) * 0.5f)) ? 6u : 0u; + } + else if (fDot >= cStepsDiv) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] >= (fY + MAX_VALUE) * 0.5f)) ? 7u : (cSteps - 1); + } + else + { + iStep = (CGU_UINT32)(fDot + 0.5f); + } + + // steps to improve quality + if (iStep < cSteps) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + CGU_FLOAT fDiff = pSteps[iStep] - pPoints[iPoint]; + dX += fc * fDiff; + d2X += fc * fc; + dY += fd * fDiff; + d2Y += fd * fd; + } + } + + // Move endpoints + if (d2X > 0.0f) + fX -= dX / d2X; + + if (d2Y > 0.0f) + fY -= dY / d2Y; + + if (fX > fY) + { + float f = fX; + fX = fY; + fY = f; + } + + if ((dX * dX < (1.0f / 64.0f)) && (dY * dY < (1.0f / 64.0f))) + break; + } + + fendpoints.x = (fX < MIN_VALUE) ? MIN_VALUE : (fX > MAX_VALUE) ? MAX_VALUE : fX; + fendpoints.y = (fY < MIN_VALUE) ? MIN_VALUE : (fY > MAX_VALUE) ? MAX_VALUE : fY; + + return fendpoints; +} + +static CGU_Vec2i cmp_findEndpointsAlphaBlockSnorm(CGU_FLOAT alphaBlockSnorm[BLOCK_SIZE_4X4]) +{ + //================================================================ + // Bounding Box + // lowest quality calculation to get min and max value to use + //================================================================ + CGU_Vec2f cmpMinMax; + cmpMinMax.x = alphaBlockSnorm[0]; + cmpMinMax.y = alphaBlockSnorm[0]; + + for (CGU_UINT8 i = 0; i < BLOCK_SIZE_4X4; ++i) + { + if (alphaBlockSnorm[i] < cmpMinMax.x) + { + cmpMinMax.x = alphaBlockSnorm[i]; + } + else if (alphaBlockSnorm[i] > cmpMinMax.y) + { + cmpMinMax.y = alphaBlockSnorm[i]; + } + } + + CGU_Vec2i endpoints; + CGU_Vec2f fendpoints; + + // Are we done for lowest quality setting! + // CGU_FLOAT fquality = 1.0f; + // + // if (fquality < CMP_QUALITY2) { + // endpoints.x = (CGU_INT8)(cmpMinMax.x); + // endpoints.y = (CGU_INT8)(cmpMinMax.y); + // return endpoints; + // } + + //================================================================ + // Do more calculations to get the best min and max values to use + //================================================================ + if ((-1.0f == cmpMinMax.x || 1.0f == cmpMinMax.y)) + { + fendpoints = cmp_optimizeEndPoints(alphaBlockSnorm, 6, true); + endpoints.x = cmp_snormFloatToSInt(fendpoints.x); + endpoints.y = cmp_snormFloatToSInt(fendpoints.y); + } + else + { + fendpoints = cmp_optimizeEndPoints(alphaBlockSnorm, 8, true); + endpoints.x = cmp_snormFloatToSInt(fendpoints.y); + endpoints.y = cmp_snormFloatToSInt(fendpoints.x); + } + + return endpoints; +} + +#ifndef ASPM_HLSL +static CGU_UINT64 cmp_getBlockPackedIndicesSNorm(CGU_Vec2f alphaMinMax, CGU_FLOAT alphaBlockSnorm[BLOCK_SIZE_4X4], CGU_UINT64 data) +{ + CGU_FLOAT alpha[8]; + alpha[0] = alphaMinMax.x; + alpha[1] = alphaMinMax.y; + + if (alphaMinMax.x > alphaMinMax.y) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 6.0f + alpha[1]) / 7.0f; + alpha[3] = (alpha[0] * 5.0f + alpha[1] * 2.0f) / 7.0f; + alpha[4] = (alpha[0] * 4.0f + alpha[1] * 3.0f) / 7.0f; + alpha[5] = (alpha[0] * 3.0f + alpha[1] * 4.0f) / 7.0f; + alpha[6] = (alpha[0] * 2.0f + alpha[1] * 5.0f) / 7.0f; + alpha[7] = (alpha[0] + alpha[1] * 6.0f) / 7.0f; + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 4.0f + alpha[1]) / 5.0f; + alpha[3] = (alpha[0] * 3.0f + alpha[1] * 2.0f) / 5.0f; + alpha[4] = (alpha[0] * 2.0f + alpha[1] * 3.0f) / 5.0f; + alpha[5] = (alpha[0] + alpha[1] * 4.0f) / 5.0f; + alpha[6] = -1.0f; + alpha[7] = 1.0f; + } + + // Index all colors using best alpha value + for (CGU_UINT8 i = 0; i < BLOCK_SIZE_4X4; ++i) + { + CGU_UINT8 uBestIndex = 0; + CGU_FLOAT fBestDelta = CMP_FLOAT_MAX; + for (CGU_INT32 uIndex = 0; uIndex < 8; uIndex++) + { + CGU_FLOAT fCurrentDelta = fabs(alpha[uIndex] - alphaBlockSnorm[i]); + if (fCurrentDelta < fBestDelta) + { + uBestIndex = uIndex; + fBestDelta = fCurrentDelta; + } + } + + data &= ~((CGU_UINT64)(0x07) << (3 * i + 16)); + data |= ((CGU_UINT64)(uBestIndex) << (3 * i + 16)); + } + + return data; +} +#endif +static void cmp_getCompressedAlphaRampS(CGU_INT8 alpha[8], const CGU_UINT32 compressedBlock[2]) +{ + alpha[0] = (CGU_INT8)(compressedBlock[0] & 0xff); + alpha[1] = (CGU_INT8)((compressedBlock[0] >> 8) & 0xff); + + if (alpha[0] > alpha[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = (CGU_UINT8)((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = (CGU_UINT8)((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = (CGU_UINT8)((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = (CGU_UINT8)((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = (CGU_UINT8)((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#else + alpha[2] = static_cast((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = static_cast((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = static_cast((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = static_cast((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#endif + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = (CGU_UINT8)((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = (CGU_UINT8)((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = (CGU_UINT8)((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#else + alpha[2] = static_cast((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = static_cast((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = static_cast((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = static_cast((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#endif + alpha[6] = -128; // Bit code 110 + alpha[7] = 127; // Bit code 111 + } +} + +static void cmp_decompressAlphaBlockS(CGU_INT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_UINT32 compressedBlock[2]) +{ + CGU_UINT32 i; + CGU_INT8 alpha[8]; + cmp_getCompressedAlphaRampS(alpha, compressedBlock); + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + CGU_UINT32 index; + if (i < 5) + index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3)); + else if (i > 5) + index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3); + else + { + index = (compressedBlock[0] & 0x80000000) >> 31; + index |= (compressedBlock[1] & 0x3) << 1; + } + + alphaBlock[i] = alpha[index]; + } +} + +//============================================================================= + +// Processes Alpha Channel either as Unsigned Norm (0..1) or (Signed Norm -1..1) +static CGU_Vec2ui cmp_compressAlphaBlock(CMP_IN CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) +{ + CGU_Vec2ui CmpBlock; + + if (isSigned) + { +#ifndef ASPM_HLSL + union + { + CGU_INT32 compressedBlock[2]; + struct + { + CGU_INT8 red_0; + CGU_INT8 red_1; + CGU_UINT8 indices[6]; + }; + CGU_UINT64 data; + } BC4_Snorm_block; + +#ifndef ASPM_GPU + BC4_Snorm_block.data = 0LL; +#else + BC4_Snorm_block.data = 0; +#endif + + CGU_Vec2i reds; + reds = cmp_findEndpointsAlphaBlockSnorm(alphaBlock); + + BC4_Snorm_block.red_0 = reds.x & 0xFF; + BC4_Snorm_block.red_1 = reds.y & 0xFF; + + // check low end boundaries + if (BC4_Snorm_block.red_0 == -128) + BC4_Snorm_block.red_0 = -127; + if (BC4_Snorm_block.red_1 == -128) + BC4_Snorm_block.red_1 = -127; + + // Normalize signed int -128..127 to float -1..1 + CGU_Vec2f alphaMinMax; + alphaMinMax.x = (CGU_FLOAT)(BC4_Snorm_block.red_0) / 127.0f; + alphaMinMax.y = (CGU_FLOAT)(BC4_Snorm_block.red_1) / 127.0f; + + BC4_Snorm_block.data = cmp_getBlockPackedIndicesSNorm(alphaMinMax, alphaBlock, BC4_Snorm_block.data); + CmpBlock.x = BC4_Snorm_block.compressedBlock[0]; + CmpBlock.y = BC4_Snorm_block.compressedBlock[1]; +#else + CGU_Vec2f RampMinMax; + RampMinMax = cmp_getLinearEndPoints(alphaBlock, fquality, false); // revert code to remove the false param + CmpBlock = cmp_getBlockPackedIndices(RampMinMax, alphaBlock, fquality); +#endif + } + else + { + CGU_Vec2f RampMinMax; + RampMinMax = cmp_getLinearEndPoints(alphaBlock, fquality, false); // revert code to remove the false param + CmpBlock = cmp_getBlockPackedIndices(RampMinMax, alphaBlock, fquality); + } + + return CmpBlock; +} + +static void cmp_getCompressedAlphaRamp(CGU_UINT8 alpha[8], const CGU_UINT32 compressedBlock[2]) +{ + alpha[0] = (CGU_UINT8)(compressedBlock[0] & 0xff); + alpha[1] = (CGU_UINT8)((compressedBlock[0] >> 8) & 0xff); + + if (alpha[0] > alpha[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = (CGU_UINT8)((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = (CGU_UINT8)((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = (CGU_UINT8)((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = (CGU_UINT8)((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = (CGU_UINT8)((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#else + alpha[2] = static_cast((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = static_cast((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = static_cast((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = static_cast((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#endif + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = (CGU_UINT8)((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = (CGU_UINT8)((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = (CGU_UINT8)((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#else + alpha[2] = static_cast((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = static_cast((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = static_cast((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = static_cast((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#endif + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } +} + +static void cmp_decompressAlphaBlock(CGU_UINT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_UINT32 compressedBlock[2]) +{ + CGU_UINT32 i; + CGU_UINT8 alpha[8]; + cmp_getCompressedAlphaRamp(alpha, compressedBlock); + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + CGU_UINT32 index; + if (i < 5) + index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3)); + else if (i > 5) + index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3); + else + { + index = (compressedBlock[0] & 0x80000000) >> 31; + index |= (compressedBlock[1] & 0x3) << 1; + } + + alphaBlock[i] = alpha[index]; + } +} + +static void cmp_ProcessColors(CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMin, + CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMax, + CMP_INOUT CGU_UINT32 CMP_PTRINOUT c0, + CMP_INOUT CGU_UINT32 CMP_PTRINOUT c1, + CGU_INT setopt, + CGU_BOOL isSRGB) +{ + // CGU_UINT32 srbMap[32] = {0,5,8,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31}; + // CGU_UINT32 sgMap[64] = {0,10,14,16,19,20,22,24,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45, + // 46,47,47,48,48,49,50,50,51,52,52,53,53,54,54,55,55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63,63}; + CGU_INT32 x, y, z; + CGU_Vec3f scale = {31.0f, 63.0f, 31.0f}; + CGU_Vec3f MinColorScaled; + CGU_Vec3f MaxColorScaled; + + // Clamp or Transform is needed, the transforms have built in clamps + if (isSRGB) + { + MinColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMin); + MaxColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMax); + } + else + { + MinColorScaled = cmp_clampVec3f(CMP_PTRINOUT colorMin, 0.0f, 1.0f); + MaxColorScaled = cmp_clampVec3f(CMP_PTRINOUT colorMax, 0.0f, 1.0f); + } + + switch (setopt) + { + case 0: // Use Min Max processing + MinColorScaled = floor(MinColorScaled * scale); + MaxColorScaled = ceil(MaxColorScaled * scale); + CMP_PTRINOUT colorMin = MinColorScaled / scale; + CMP_PTRINOUT colorMax = MaxColorScaled / scale; + break; + default: // Use round processing + MinColorScaled = round(MinColorScaled * scale); + MaxColorScaled = round(MaxColorScaled * scale); + break; + } + + x = (CGU_UINT32)(MinColorScaled.x); + y = (CGU_UINT32)(MinColorScaled.y); + z = (CGU_UINT32)(MinColorScaled.z); + + //if (isSRGB) { + // // scale RB + // x = srbMap[x]; // &0x1F]; + // y = sgMap [y]; // &0x3F]; + // z = srbMap[z]; // &0x1F]; + // // scale G + //} + CMP_PTRINOUT c0 = (x << 11) | (y << 5) | z; + + x = (CGU_UINT32)(MaxColorScaled.x); + y = (CGU_UINT32)(MaxColorScaled.y); + z = (CGU_UINT32)(MaxColorScaled.z); + CMP_PTRINOUT c1 = (x << 11) | (y << 5) | z; +} + +#ifndef ASPM_GPU // Used by BC1, BC2 & BC3 +//---------------------------------------------------- +// This function decompresses a DXT colour block +// The block is decompressed to 8 bits per channel +// Result buffer is RGBA format, A is set to 255 +//---------------------------------------------------- +static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], const CGU_Vec2ui compressedBlock, const CGU_BOOL mapDecodeRGBA) +{ + CGU_BOOL bDXT1 = TRUE; + CGU_UINT32 n0 = compressedBlock.x & 0xffff; + CGU_UINT32 n1 = compressedBlock.x >> 16; + CGU_UINT32 r0; + CGU_UINT32 g0; + CGU_UINT32 b0; + CGU_UINT32 r1; + CGU_UINT32 g1; + CGU_UINT32 b1; + + r0 = ((n0 & 0xf800) >> 8); + g0 = ((n0 & 0x07e0) >> 3); + b0 = ((n0 & 0x001f) << 3); + + r1 = ((n1 & 0xf800) >> 8); + g1 = ((n1 & 0x07e0) >> 3); + b1 = ((n1 & 0x001f) << 3); + + // Apply the lower bit replication to give full dynamic range + r0 += (r0 >> 5); + r1 += (r1 >> 5); + g0 += (g0 >> 6); + g1 += (g1 >> 6); + b0 += (b0 >> 5); + b1 += (b1 >> 5); + + if (!mapDecodeRGBA) + { + //-------------------------------------------------------------- + // Channel mapping output as BGRA + //-------------------------------------------------------------- + CGU_UINT32 c0 = 0xff000000 | (r0 << 16) | (g0 << 8) | b0; + CGU_UINT32 c1 = 0xff000000 | (r1 << 16) | (g1 << 8) | b1; + + if (!bDXT1 || n0 > n1) + { + CGU_UINT32 c2 = 0xff000000 | (((2 * r0 + r1) / 3) << 16) | (((2 * g0 + g1) / 3) << 8) | (((2 * b0 + b1) / 3)); + CGU_UINT32 c3 = 0xff000000 | (((2 * r1 + r0) / 3) << 16) | (((2 * g1 + g0) / 3) << 8) | (((2 * b1 + b0) / 3)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + + switch (index) + { + case 0: + ((CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CGU_UINT32*)rgbBlock)[i] = c3; + break; + } + } + } + else + { + // Transparent decode + CGU_UINT32 c2 = 0xff000000 | (((r0 + r1) / 2) << 16) | (((g0 + g1) / 2) << 8) | (((b0 + b1) / 2)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + + switch (index) + { + case 0: + ((CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CGU_UINT32*)rgbBlock)[i] = 0x00000000; + break; + } + } + } + } + else + { + // MAP_BC15_TO_ABGR + //-------------------------------------------------------------- + // Channel mapping output as RGBA + //-------------------------------------------------------------- + + CGU_UINT32 c0 = 0xff000000 | (b0 << 16) | (g0 << 8) | r0; + CGU_UINT32 c1 = 0xff000000 | (b1 << 16) | (g1 << 8) | r1; + + if (!bDXT1 || n0 > n1) + { + CGU_UINT32 c2 = 0xff000000 | (((2 * b0 + b1 + 1) / 3) << 16) | (((2 * g0 + g1 + 1) / 3) << 8) | (((2 * r0 + r1 + 1) / 3)); + CGU_UINT32 c3 = 0xff000000 | (((2 * b1 + b0 + 1) / 3) << 16) | (((2 * g1 + g0 + 1) / 3) << 8) | (((2 * r1 + r0 + 1) / 3)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c3; + break; + } + } + } + else + { + // Transparent decode + CGU_UINT32 c2 = 0xff000000 | (((b0 + b1) / 2) << 16) | (((g0 + g1) / 2) << 8) | (((r0 + r1) / 2)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = 0x00000000; + break; + } + } + } + } //MAP_ABGR +} +#endif // !ASPM_GPU + +//-------------------------------------------------------------------------------------------------------- +// Decompress is RGB (0.0f..255.0f) +//-------------------------------------------------------------------------------------------------------- +static void cmp_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock) +{ + CGU_UINT32 n0 = compressedBlock.x & 0xffff; + CGU_UINT32 n1 = compressedBlock.x >> 16; + CGU_UINT32 index; + + //------------------------------------------------------- + // Decode the compressed block 0..255 color range + //------------------------------------------------------- + CGU_Vec3f c0 = cmp_565ToLinear(n0); // max color + CGU_Vec3f c1 = cmp_565ToLinear(n1); // min color + CGU_Vec3f c2; + CGU_Vec3f c3; + + if (n0 > n1) + { + c2 = (c0 * 2.0f + c1) / 3.0f; + c3 = (c1 * 2.0f + c0) / 3.0f; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + rgbBlock[i] = c0; + break; + case 1: + rgbBlock[i] = c1; + break; + case 2: + rgbBlock[i] = c2; + break; + case 3: + rgbBlock[i] = c3; + break; + } + } + } + else + { + // Transparent decode + c2 = (c0 + c1) / 2.0f; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + rgbBlock[i] = c0; + break; + case 1: + rgbBlock[i] = c1; + break; + case 2: + rgbBlock[i] = c2; + break; + case 3: + rgbBlock[i] = 0.0f; + break; + } + } + } +} + +// The source is 0..1, decompressed data using cmp_decompressRGBBlock is 0..255 which is converted down to 0..1 +static float CMP_RGBBlockError(const CGU_Vec3f src_rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock, CGU_BOOL isSRGB) +{ + CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4]; + + // Decompressed block channels are 0..255 + cmp_decompressRGBBlock(rgbBlock, compressedBlock); + + //------------------------------------------------------------------ + // Calculate MSE of the block + // Note : pow is used as Float type for the code to be usable on CPU + //------------------------------------------------------------------ + CGU_Vec3f serr; + serr = 0.0f; + + float sR, sG, sB, R, G, B; + + for (int j = 0; j < 16; j++) + { + if (isSRGB) + { + sR = round(cmp_linearToSrgbf(src_rgbBlock[j].x) * 255.0f); + sG = round(cmp_linearToSrgbf(src_rgbBlock[j].y) * 255.0f); + sB = round(cmp_linearToSrgbf(src_rgbBlock[j].z) * 255.0f); + } + else + { + sR = round(src_rgbBlock[j].x * 255.0f); + sG = round(src_rgbBlock[j].y * 255.0f); + sB = round(src_rgbBlock[j].z * 255.0f); + } + + rgbBlock[j] = rgbBlock[j]; + + R = rgbBlock[j].x; + G = rgbBlock[j].y; + B = rgbBlock[j].z; + + // Norm colors + serr.x += pow(sR - R, 2.0f); + serr.y += pow(sG - G, 2.0f); + serr.z += pow(sB - B, 2.0f); + } + + // MSE for 16 texels + return (serr.x + serr.y + serr.z) / 48.0f; +} + +// Processing input source 0..1.0f) +static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB, CMP_INOUT CGU_FLOAT CMP_PTRINOUT errout) +{ + CGU_Vec3f axisVectorRGB = {0.0f, 0.0f, 0.0f}; // The axis vector for index projection + CGU_FLOAT pos_on_axis[16]; // The distance each unique falls along the compression axis + CGU_FLOAT axisleft = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis + CGU_FLOAT axisright = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis + CGU_FLOAT axiscentre = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis + CGU_INT32 swap = 0; // Indicator if the RGB values need swapping to generate an opaque result + CGU_Vec3f average_rgb; // The centrepoint of the axis + CGU_Vec3f srcRGB[16]; // The list of source colors with blue channel altered + CGU_Vec3f srcBlock[16]; // The list of source colors with any color space transforms and clipping + CGU_Vec3f rgb; + CGU_UINT32 c0 = 0; + CGU_UINT32 c1 = 0; + CGU_Vec2ui compressedBlock = {0, 0}; + CGU_FLOAT Q1CompErr = CMP_FLT_MAX; + CGU_Vec2ui Q1CompData = {0,0}; + + // ------------------------------------------------------------------------------------- + // (1) Find the array of unique pixel values and sum them to find their average position + // ------------------------------------------------------------------------------------- + { + CGU_FLOAT errLQ = 0.0f; + CGU_BOOL fastProcess = (fquality <= CMP_QUALITY1); + CGU_Vec3f srcMin = 1.0f; // Min source color + CGU_Vec3f srcMax = 0.0f; // Max source color + CGU_Vec2ui Q1compressedBlock = {0, 0}; + + average_rgb = 0.0f; + // Get average and modifed src + // find average position and save list of pixels as 0F..255F range for processing + // Note: z (blue) is average of blue+green channels + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + srcMin = cmp_minVec3f(srcMin, rgbBlockUVf[i]); + srcMax = cmp_maxVec3f(srcMax, rgbBlockUVf[i]); + if (!fastProcess) + { + rgb = isSRGB ? cmp_linearToSrgb(rgbBlockUVf[i]) : cmp_saturate(rgbBlockUVf[i]); + rgb.z = (rgb.y + rgb.z) * 0.5F; // Z-axiz => (R+G)/2 + srcRGB[i] = rgb; + average_rgb = average_rgb + rgb; + } + } + + // Process two colors for saving in 565 format as C0 and C1 + cmp_ProcessColors(CMP_REFINOUT srcMin, CMP_REFINOUT srcMax, CMP_REFINOUT c0, CMP_REFINOUT c1, isSRGB ? 1 : 0, isSRGB); + + // Save simple min-max encoding + CGU_UINT32 index = 0; + if (c0 < c1) + { + Q1CompData.x = (c0 << 16) | c1; + errLQ = cmp_getIndicesRGB(CMP_REFINOUT index, rgbBlockUVf, srcMin, srcMax, false); + Q1CompData.y = index; + CMP_PTRINOUT errout = errLQ; + } + else + { + // Most simple case all colors are equal or 0.0f + Q1compressedBlock.x = (c1 << 16) | c0; + Q1compressedBlock.y = 0; + CMP_PTRINOUT errout = 0.0f; + return Q1compressedBlock; + } + + if (fastProcess) + return Q1CompData; + + // 0.0625F is (1/BLOCK_SIZE_4X4) + average_rgb = average_rgb * 0.0625F; + } + + // ------------------------------------------------------------------------------------- + // (4) For each component, reflect points about the average so all lie on the same side + // of the average, and compute the new average - this gives a second point that defines the axis + // To compute the sign of the axis sum the positive differences of G for each of R and B (the + // G axis is always positive in this implementation + // ------------------------------------------------------------------------------------- + // An interesting situation occurs if the G axis contains no information, in which case the RB + // axis is also compared. I am not entirely sure if this is the correct implementation - should + // the priority axis be determined by magnitude? + { + CGU_FLOAT rg_pos = 0.0f; + CGU_FLOAT bg_pos = 0.0f; + CGU_FLOAT rb_pos = 0.0f; + + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + rgb = srcRGB[i] - average_rgb; + axisVectorRGB = axisVectorRGB + fabs(rgb); + if (rgb.x > 0) + { + rg_pos += rgb.y; + rb_pos += rgb.z; + } + if (rgb.z > 0) + bg_pos += rgb.y; + } + + // Average over BLOCK_SIZE_4X4 + axisVectorRGB = axisVectorRGB * 0.0625F; + + // New average position + if (rg_pos < 0) + axisVectorRGB.x = -axisVectorRGB.x; + if (bg_pos < 0) + axisVectorRGB.z = -axisVectorRGB.z; + if ((rg_pos == bg_pos) && (rg_pos == 0)) + { + if (rb_pos < 0) + axisVectorRGB.z = -axisVectorRGB.z; + } + } + + // ------------------------------------------------------------------------------------- + // (5) Axis projection and remapping + // ------------------------------------------------------------------------------------- + { + CGU_FLOAT v2_recip; + // Normalize the axis for simplicity of future calculation + v2_recip = dot(axisVectorRGB, axisVectorRGB); + if (v2_recip > 0) + v2_recip = 1.0f / (CGU_FLOAT)sqrt(v2_recip); + else + v2_recip = 1.0f; + axisVectorRGB = axisVectorRGB * v2_recip; + } + + // ------------------------------------------------------------------------------------- + // (6) Map the axis + // ------------------------------------------------------------------------------------- + // the line joining (and extended on either side of) average and axis + // defines the axis onto which the points will be projected + // Project all the points onto the axis, calculate the distance along + // the axis from the centre of the axis (average) + // From Foley & Van Dam: Closest point of approach of a line (P + v) to a point (R) is + // P + ((R-P).v) / (v.v))v + // The distance along v is therefore (R-P).v / (v.v) where (v.v) is 1 if v is a unit vector. + // + // Calculate the extremities at the same time - these need to be reasonably accurately + // represented in all cases + { + axisleft = CMP_FLOAT_MAX; + axisright = -CMP_FLOAT_MAX; + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + // Compute the distance along the axis of the point of closest approach + CGU_Vec3f temp = (srcRGB[i] - average_rgb); + pos_on_axis[i] = dot(temp, axisVectorRGB); + + // Work out the extremities + if (pos_on_axis[i] < axisleft) + axisleft = pos_on_axis[i]; + if (pos_on_axis[i] > axisright) + axisright = pos_on_axis[i]; + } + } + + // --------------------------------------------------------------------------------------------- + // (7) Now we have a good axis and the basic information about how the points are mapped to it + // Our initial guess is to represent the endpoints accurately, by moving the average + // to the centre and recalculating the point positions along the line + // --------------------------------------------------------------------------------------------- + { + axiscentre = (axisleft + axisright) * 0.5F; + average_rgb = average_rgb + (axisVectorRGB * axiscentre); + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + pos_on_axis[i] -= axiscentre; + axisright -= axiscentre; + axisleft -= axiscentre; + } + + // ------------------------------------------------------------------------------------- + // (8) Calculate the high and low output colour values + // Involved in this is a rounding procedure which is undoubtedly slightly twitchy. A + // straight rounded average is not correct, as the decompressor 'unrounds' by replicating + // the top bits to the bottom. + // In order to take account of this process, we don't just apply a straight rounding correction, + // but base our rounding on the input value (a straight rounding is actually pretty good in terms of + // error measure, but creates a visual colour and/or brightness shift relative to the original image) + // The method used here is to apply a centre-biased rounding dependent on the input value, which was + // (mostly by experiment) found to give minimum MSE while preserving the visual characteristics of + // the image. + // rgb = (average_rgb + (left|right)*axisVectorRGB); + // ------------------------------------------------------------------------------------- + { + CGU_Vec3f MinColor, MaxColor; + + MinColor = average_rgb + (axisVectorRGB * axisleft); + MaxColor = average_rgb + (axisVectorRGB * axisright); + MinColor.z = (MinColor.z * 2) - MinColor.y; + MaxColor.z = (MaxColor.z * 2) - MaxColor.y; + + cmp_ProcessColors(CMP_REFINOUT MinColor, CMP_REFINOUT MaxColor, CMP_REFINOUT c0, CMP_REFINOUT c1, 1, false); + + // Force to be a 4-colour opaque block - in which case, c0 is greater than c1 + swap = 0; + if (c0 < c1) + { + CGU_UINT32 t; + t = c0; + c0 = c1; + c1 = t; + swap = 1; + } + else if (c0 == c1) + { + // This block will always be encoded in 3-colour mode + // Need to ensure that only one of the two points gets used, + // avoiding accidentally setting some transparent pixels into the block + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + pos_on_axis[i] = axisleft; + } + + compressedBlock.x = c0 | (c1 << 16); + + // ------------------------------------------------------------------------------------- + // (9) Final clustering, creating the 2-bit values that define the output + // ------------------------------------------------------------------------------------- + + CGU_UINT32 index; + CGU_FLOAT division; + { + compressedBlock.y = 0; + division = axisright * 2.0f / 3.0f; + axiscentre = (axisleft + axisright) / 2; // Actually, this code only works if centre is 0 or approximately so + + CGU_FLOAT CompMinErr; + + // This feature is work in progress + // remap to BC1 spec for decoding offsets, + // where cn[0] > cn[1] Max Color = index 0, 2/3 offset =index 2, 1/3 offset = index 3, Min Color = index 1 + // CGU_Vec3f cn[4]; + // cn[0] = MaxColor; + // cn[1] = MinColor; + // cn[2] = cn[0]*2.0f/3.0f + cn[1]*1.0f/3.0f; + // cn[3] = cn[0]*1.0f/3.0f + cn[1]*2.0f/3.0f; + + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + // Endpoints (indicated by block > average) are 0 and 1, while + // interpolants are 2 and 3 + if (fabs(pos_on_axis[i]) >= division) + index = 0; + else + index = 2; + // Positive is in the latter half of the block + if (pos_on_axis[i] >= axiscentre) + index += 1; + + index = index ^ swap; + // Set the output, taking swapping into account + compressedBlock.y |= (index << (2 * i)); + + // use err calc for use in higher quality code + //CompMinErr += dot(srcRGBRef[i] - cn[index],srcRGBRef[i] - cn[index]); + } + + //CompMinErr = CompMinErr * 0.0208333f; + + CompMinErr = CMP_RGBBlockError(rgbBlockUVf, compressedBlock, isSRGB); + Q1CompErr = CMP_RGBBlockError(rgbBlockUVf, Q1CompData, isSRGB); + + if (CompMinErr > Q1CompErr) + { + compressedBlock = Q1CompData; + CMP_PTRINOUT errout = Q1CompErr; + } + else + CMP_PTRINOUT errout = CompMinErr; + } + } + // done + + return compressedBlock; +} + +#ifndef CMP_USE_LOWQUALITY +static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X4], + CGU_FLOAT Rpt[BLOCK_SIZE_4X4], + CGU_UINT32 dwUniqueColors, + CGU_Vec3f channelWeightsBGR, + CGU_UINT32 m_nRefinementSteps) +{ + CMP_UNUSED(channelWeightsBGR); + CMP_UNUSED(m_nRefinementSteps); + ALIGN_16 CGU_FLOAT Prj0[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT Prj[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT PrjErr[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT RmpIndxs[BLOCK_SIZE_4X4]; + + CGU_Vec3f LineDirG; + CGU_Vec3f LineDir; + CGU_FLOAT LineDir0[NUM_CHANNELS]; + CGU_Vec3f BlkUV[BLOCK_SIZE_4X4]; + CGU_Vec3f BlkSh[BLOCK_SIZE_4X4]; + CGU_Vec3f Mdl; + + CGU_Vec3f rsltC0; + CGU_Vec3f rsltC1; + CGU_Vec3f PosG0 = {0.0f, 0.0f, 0.0f}; + CGU_Vec3f PosG1 = {0.0f, 0.0f, 0.0f}; + CGU_UINT32 i; + + for (i = 0; i < dwUniqueColors; i++) + { + BlkUV[i] = BlkInBGRf_UV[i]; + } + + // if not more then 2 different colors, we've done + if (dwUniqueColors <= 2) + { + rsltC0 = BlkInBGRf_UV[0] * 255.0f; + rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f; + } + else + { + // This is our first attempt to find an axis we will go along. + // The cumulation is done to find a line minimizing the MSE from the + // input 3D points. + + // While trying to find the axis we found that the diameter of the input + // set is quite small. Do not bother. + + // FindAxisIsSmall(BlkSh, LineDir0, Mdl, Blk, Rpt,dwUniqueColors); + { + CGU_UINT32 ii; + CGU_UINT32 jj; + CGU_UINT32 kk; + + // These vars cannot be Vec3 as index to them are varying + CGU_FLOAT Crrl[NUM_CHANNELS]; + CGU_FLOAT RGB2[NUM_CHANNELS]; + + LineDir0[0] = LineDir0[1] = LineDir0[2] = RGB2[0] = RGB2[1] = RGB2[2] = Crrl[0] = Crrl[1] = Crrl[2] = Mdl.x = Mdl.y = Mdl.z = 0.f; + + // sum position of all points + CGU_FLOAT fNumPoints = 0.0f; + for (ii = 0; ii < dwUniqueColors; ii++) + { + Mdl.x += BlkUV[ii].x * Rpt[ii]; + Mdl.y += BlkUV[ii].y * Rpt[ii]; + Mdl.z += BlkUV[ii].z * Rpt[ii]; + fNumPoints += Rpt[ii]; + } + + // and then average to calculate center coordinate of block + Mdl /= fNumPoints; + + for (ii = 0; ii < dwUniqueColors; ii++) + { + // calculate output block as offsets around block center + BlkSh[ii] = BlkUV[ii] - Mdl; + + // compute correlation matrix + // RGB2 = sum of ((distance from point from center) squared) + RGB2[0] += BlkSh[ii].x * BlkSh[ii].x * Rpt[ii]; + RGB2[1] += BlkSh[ii].y * BlkSh[ii].y * Rpt[ii]; + RGB2[2] += BlkSh[ii].z * BlkSh[ii].z * Rpt[ii]; + + Crrl[0] += BlkSh[ii].x * BlkSh[ii].y * Rpt[ii]; + Crrl[1] += BlkSh[ii].y * BlkSh[ii].z * Rpt[ii]; + Crrl[2] += BlkSh[ii].z * BlkSh[ii].x * Rpt[ii]; + } + + // if set's diameter is small + CGU_UINT32 i0 = 0, i1 = 1; + CGU_FLOAT mxRGB2 = 0.0f; + + CGU_FLOAT fEPS = fNumPoints * EPS; + for (kk = 0, jj = 0; jj < 3; jj++) + { + if (RGB2[jj] >= fEPS) + kk++; + else + RGB2[jj] = 0.0f; + + if (mxRGB2 < RGB2[jj]) + { + mxRGB2 = RGB2[jj]; + i0 = jj; + } + } + + CGU_FLOAT fEPS2 = fNumPoints * EPS2; + CGU_BOOL AxisIsSmall; + + AxisIsSmall = (RGB2[0] < fEPS2); + AxisIsSmall = AxisIsSmall && (RGB2[1] < fEPS2); + AxisIsSmall = AxisIsSmall && (RGB2[2] < fEPS2); + + // all are very small to avoid division on the small determinant + if (AxisIsSmall) + { + rsltC0 = BlkInBGRf_UV[0] * 255.0f; + rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f; + } + else + { + // !AxisIsSmall + if (kk == 1) // really only 1 dimension + LineDir0[i0] = 1.; + else if (kk == 2) + { // really only 2 dimensions + i1 = (RGB2[(i0 + 1) % 3] > 0.f) ? (i0 + 1) % 3 : (i0 + 2) % 3; + CGU_FLOAT Crl = (i1 == (i0 + 1) % 3) ? Crrl[i0] : Crrl[(i0 + 2) % 3]; + LineDir0[i1] = Crl / RGB2[i0]; + LineDir0[i0] = 1.; + } + else + { + CGU_FLOAT maxDet = 100000.f; + CGU_FLOAT Cs[3]; + // select max det for precision + for (jj = 0; jj < 3; jj++) + { + // 3 = nDimensions + CGU_FLOAT Det = RGB2[jj] * RGB2[(jj + 1) % 3] - Crrl[jj] * Crrl[jj]; + Cs[jj] = fabs(Crrl[jj] / sqrt(RGB2[jj] * RGB2[(jj + 1) % 3])); + if (maxDet < Det) + { + maxDet = Det; + i0 = jj; + } + } + + // inverse correl matrix + // -- -- -- -- + // | A B | | C -B | + // | B C | => | -B A | + // -- -- -- -- + CGU_FLOAT mtrx1[2][2]; + CGU_FLOAT vc1[2]; + CGU_FLOAT vc[2]; + vc1[0] = Crrl[(i0 + 2) % 3]; + vc1[1] = Crrl[(i0 + 1) % 3]; + // C + mtrx1[0][0] = RGB2[(i0 + 1) % 3]; + // A + mtrx1[1][1] = RGB2[i0]; + // -B + mtrx1[1][0] = mtrx1[0][1] = -Crrl[i0]; + // find a solution + vc[0] = mtrx1[0][0] * vc1[0] + mtrx1[0][1] * vc1[1]; + vc[1] = mtrx1[1][0] * vc1[0] + mtrx1[1][1] * vc1[1]; + // normalize + vc[0] /= maxDet; + vc[1] /= maxDet; + // find a line direction vector + LineDir0[i0] = 1.; + LineDir0[(i0 + 1) % 3] = 1.; + LineDir0[(i0 + 2) % 3] = vc[0] + vc[1]; + } + + // normalize direction vector + CGU_FLOAT Len = LineDir0[0] * LineDir0[0] + LineDir0[1] * LineDir0[1] + LineDir0[2] * LineDir0[2]; + Len = sqrt(Len); + + LineDir0[0] = (Len > 0.f) ? LineDir0[0] / Len : 0.0f; + LineDir0[1] = (Len > 0.f) ? LineDir0[1] / Len : 0.0f; + LineDir0[2] = (Len > 0.f) ? LineDir0[2] / Len : 0.0f; + } + } // FindAxisIsSmall + + // GCC is being an awful being when it comes to goto-jumps. + // So please bear with this. + CGU_FLOAT ErrG = 10000000.f; + CGU_FLOAT PrjBnd0; + CGU_FLOAT PrjBnd1; + ALIGN_16 CGU_FLOAT PreMRep[BLOCK_SIZE_4X4]; + + LineDir.x = LineDir0[0]; + LineDir.y = LineDir0[1]; + LineDir.z = LineDir0[2]; + + // Here is the main loop. + // 1. Project input set on the axis in consideration. + // 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal pair of end points. + // 3. Compute the vector of indexes (or clusters) for the current approximate ramp. + // 4. Present our color channels as 3 16DIM vectors. + // 5. Find closest approximation of each of 16DIM color vector with the projection of the 16DIM index vector. + // 6. Plug the projections as a new directional vector for the axis. + // 7. Goto 1. + // D - is 16 dim "index" vector (or 16 DIM vector of indexes - {0, 1/3,2/3, 0, ...,}, but shifted and normalized). + // Ci - is a 16 dim vector of color i. for each Ci find a scalar Ai such that (Ai * D - Ci) (Ai * D - Ci) -> min , + // i.e distance between vector AiD and C is min. You can think of D as a unit interval(vector) "clusterizer", and Ai is a scale + // you need to apply to the clusterizer to approximate the Ci vector instead of the unit vector. + // Solution is + // Ai = (D . Ci) / (D . D); . - is a dot product. + // in 3 dim space Ai(s) represent a line direction, along which + // we again try to find (sub)optimal quantizer. + // That's what our for(;;) loop is about. + for (;;) + { + // 1. Project input set on the axis in consideration. + // From Foley & Van Dam: Closest point of approach of a line (P + v) to a + // point (R) is + // P + ((R-P).v) / (v.v))v + // The distance along v is therefore (R-P).v / (v.v) + // (v.v) is 1 if v is a unit vector. + // + PrjBnd0 = 1000.0f; + PrjBnd1 = -1000.0f; + for (i = 0; i < BLOCK_SIZE_4X4; i++) + Prj0[i] = Prj[i] = PrjErr[i] = PreMRep[i] = 0.f; + + for (i = 0; i < dwUniqueColors; i++) + { + Prj0[i] = Prj[i] = dot(BlkSh[i], LineDir); + PrjErr[i] = dot(BlkSh[i] - LineDir * Prj[i], BlkSh[i] - LineDir * Prj[i]); + PrjBnd0 = min(PrjBnd0, Prj[i]); + PrjBnd1 = max(PrjBnd1, Prj[i]); + } + + // 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal + // pair of end points. + + // min and max of the search interval + CGU_FLOAT Scl0; + CGU_FLOAT Scl1; + Scl0 = PrjBnd0 - (PrjBnd1 - PrjBnd0) * 0.125f; + Scl1 = PrjBnd1 + (PrjBnd1 - PrjBnd0) * 0.125f; + + // compute scaling factor to scale down the search interval to [0.,1] + const CGU_FLOAT Scl2 = (Scl1 - Scl0) * (Scl1 - Scl0); + const CGU_FLOAT overScl = 1.f / (Scl1 - Scl0); + + for (i = 0; i < dwUniqueColors; i++) + { + // scale them + Prj[i] = (Prj[i] - Scl0) * overScl; + // premultiply the scale square to plug into error computation later + PreMRep[i] = Rpt[i] * Scl2; + } + + // scale first approximation of end points + PrjBnd0 = (PrjBnd0 - Scl0) * overScl; + PrjBnd1 = (PrjBnd1 - Scl0) * overScl; + + CGU_FLOAT StepErr = MAX_ERROR; + + // search step + CGU_FLOAT searchStep = 0.025f; + + // low Start/End; high Start/End + const CGU_FLOAT lowStartEnd = (PrjBnd0 - 2.f * searchStep > 0.f) ? PrjBnd0 - 2.f * searchStep : 0.f; + const CGU_FLOAT highStartEnd = (PrjBnd1 + 2.f * searchStep < 1.f) ? PrjBnd1 + 2.f * searchStep : 1.f; + + // find the best endpoints + CGU_FLOAT Pos0 = 0; + CGU_FLOAT Pos1 = 0; + CGU_FLOAT lowPosStep, highPosStep; + CGU_FLOAT err; + + int l, h; + for (l = 0, lowPosStep = lowStartEnd; l < 8; l++, lowPosStep += searchStep) + { + for (h = 0, highPosStep = highStartEnd; h < 8; h++, highPosStep -= searchStep) + { + // compute an error for the current pair of end points. + err = cmp_getRampErr(Prj, PrjErr, PreMRep, StepErr, lowPosStep, highPosStep, dwUniqueColors); + + if (err < StepErr) + { + // save better result + StepErr = err; + Pos0 = lowPosStep; + Pos1 = highPosStep; + } + } + } + + // inverse the scaling + Pos0 = Pos0 * (Scl1 - Scl0) + Scl0; + Pos1 = Pos1 * (Scl1 - Scl0) + Scl0; + + // did we find somthing better from the previous run? + if (StepErr + 0.001 < ErrG) + { + // yes, remember it + ErrG = StepErr; + LineDirG = LineDir; + + PosG0.x = Pos0; + PosG0.y = Pos0; + PosG0.z = Pos0; + PosG1.x = Pos1; + PosG1.y = Pos1; + PosG1.z = Pos1; + + // 3. Compute the vector of indexes (or clusters) for the current + // approximate ramp. + // indexes + const CGU_FLOAT step = (Pos1 - Pos0) / 3.0f; // (dwNumChannels=4 - 1); + const CGU_FLOAT step_h = step * (CGU_FLOAT)0.5; + const CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step; + const CGU_FLOAT overBlkTp = 1.f / 3.0f; // (dwNumChannels=4 - 1); + + // here the index vector is computed, + // shifted and normalized + CGU_FLOAT indxAvrg = 3.0f / 2.0f; // (dwNumChannels=4 - 1); + + for (i = 0; i < dwUniqueColors; i++) + { + CGU_FLOAT del; + // CGU_UINT32 n = (CGU_UINT32)((b - _min_ex + (step*0.5f)) * rstep); + if ((del = Prj0[i] - Pos0) <= 0) + RmpIndxs[i] = 0.f; + else if (Prj0[i] - Pos1 >= 0) + RmpIndxs[i] = 3.0f; // (dwNumChannels=4 - 1); + else + RmpIndxs[i] = floor((del + step_h) * rstep); + // shift and normalization + RmpIndxs[i] = (RmpIndxs[i] - indxAvrg) * overBlkTp; + } + + // 4. Present our color channels as 3 16 DIM vectors. + // 5. Find closest aproximation of each of 16DIM color vector with the + // pojection of the 16DIM index vector. + CGU_Vec3f Crs = {0.0f, 0.0f, 0.0f}; + CGU_FLOAT Len = 0.0f; + + for (i = 0; i < dwUniqueColors; i++) + { + const CGU_FLOAT PreMlt = RmpIndxs[i] * Rpt[i]; + Len += RmpIndxs[i] * PreMlt; + Crs.x += BlkSh[i].x * PreMlt; + Crs.y += BlkSh[i].y * PreMlt; + Crs.z += BlkSh[i].z * PreMlt; + } + + LineDir.x = LineDir.y = LineDir.z = 0.0f; + if (Len > 0.0f) + { + CGU_FLOAT Len2; + LineDir = Crs / Len; + // 6. Plug the projections as a new directional vector for the axis. + // 7. Goto 1. + Len2 = dot(LineDir, LineDir); // LineDir.x * LineDir.x + LineDir.y * LineDir.y + LineDir.z * LineDir.z; + Len2 = sqrt(Len2); + LineDir /= Len2; + } + } + else // We was not able to find anything better. Drop out. + break; + } + + // inverse transform to find end-points of 3-color ramp + rsltC0 = (PosG0 * LineDirG + Mdl) * 255.f; + rsltC1 = (PosG1 * LineDirG + Mdl) * 255.f; + } // !isDone + + // We've dealt with (almost) unrestricted full precision realm. + // Now back digital world. + + // round the end points to make them look like compressed ones + CGU_Vec3f inpRmpEndPts0 = {0.0f, 255.0f, 0.0f}; + CGU_Vec3f inpRmpEndPts1 = {0.0f, 255.0f, 0.0f}; + CGU_Vec3f Fctrs0 = {8.0f, 4.0f, 8.0f}; //(1 << (PIX_GRID - BG)); x (1 << (PIX_GRID - GG)); y (1 << (PIX_GRID - RG)); z + CGU_Vec3f Fctrs1 = {32.0f, 64.0f, 32.0f}; //(CGU_FLOAT)(1 << RG); z (CGU_FLOAT)(1 << GG); y (CGU_FLOAT)(1 << BG); x + CGU_FLOAT _Min = 0.0f; + CGU_FLOAT _Max = 255.0f; + + { + // MkRmpOnGrid(inpRmpEndPts, rsltC, _Min, _Max); + + inpRmpEndPts0 = floor(rsltC0); + + if (inpRmpEndPts0.x <= _Min) + inpRmpEndPts0.x = _Min; + else + { + inpRmpEndPts0.x += floor(128.f / Fctrs1.x) - floor(inpRmpEndPts0.x / Fctrs1.x); + inpRmpEndPts0.x = min(inpRmpEndPts0.x, _Max); + } + if (inpRmpEndPts0.y <= _Min) + inpRmpEndPts0.y = _Min; + else + { + inpRmpEndPts0.y += floor(128.f / Fctrs1.y) - floor(inpRmpEndPts0.y / Fctrs1.y); + inpRmpEndPts0.y = min(inpRmpEndPts0.y, _Max); + } + if (inpRmpEndPts0.z <= _Min) + inpRmpEndPts0.z = _Min; + else + { + inpRmpEndPts0.z += floor(128.f / Fctrs1.z) - floor(inpRmpEndPts0.z / Fctrs1.z); + inpRmpEndPts0.z = min(inpRmpEndPts0.z, _Max); + } + + inpRmpEndPts0 = floor(inpRmpEndPts0 / Fctrs0) * Fctrs0; + + inpRmpEndPts1 = floor(rsltC1); + if (inpRmpEndPts1.x <= _Min) + inpRmpEndPts1.x = _Min; + else + { + inpRmpEndPts1.x += floor(128.f / Fctrs1.x) - floor(inpRmpEndPts1.x / Fctrs1.x); + inpRmpEndPts1.x = min(inpRmpEndPts1.x, _Max); + } + if (inpRmpEndPts1.y <= _Min) + inpRmpEndPts1.y = _Min; + else + { + inpRmpEndPts1.y += floor(128.f / Fctrs1.y) - floor(inpRmpEndPts1.y / Fctrs1.y); + inpRmpEndPts1.y = min(inpRmpEndPts1.y, _Max); + } + if (inpRmpEndPts1.z <= _Min) + inpRmpEndPts1.z = _Min; + else + { + inpRmpEndPts1.z += floor(128.f / Fctrs1.z) - floor(inpRmpEndPts1.z / Fctrs1.z); + inpRmpEndPts1.z = min(inpRmpEndPts1.z, _Max); + } + + inpRmpEndPts1 = floor(inpRmpEndPts1 / Fctrs0) * Fctrs0; + } // MkRmpOnGrid + + CMP_EndPoints EndPoints; + EndPoints.Color0 = inpRmpEndPts0; + EndPoints.Color1 = inpRmpEndPts1; + + return EndPoints; +} +#endif + +// Process a rgbBlock which is normalized (0.0f ... 1.0f), signed normal is not implemented +static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLOCK_SIZE_4X4], + const CGU_FLOAT BlockA[BLOCK_SIZE_4X4], + CGU_Vec3f channelWeights, + CGU_UINT32 dwAlphaThreshold, + CGU_UINT32 m_nRefinementSteps, + CMP_IN CGU_FLOAT fquality, + CGU_BOOL isSRGB) +{ + CGU_Vec2ui cmpBlock = {0, 0}; + CGU_FLOAT errLQ = 1e6f; + + cmpBlock = CompressRGBBlock_FM(rgbBlockUVf, fquality, isSRGB, CMP_REFINOUT errLQ); + +#ifndef CMP_USE_LOWQUALITY + //------------------------------------------------------------------ + // Processing is in 0..255 range, code needs to be normized to 0..1 + //------------------------------------------------------------------ + if ((errLQ > 0.0f) && (fquality > CMP_QUALITY2)) + { + CGU_Vec3f rgbBlock_normal[BLOCK_SIZE_4X4]; + CGU_UINT32 nCmpIndices = 0; + CGU_UINT32 c0, c1; + // High Quality + CMP_EndPoints EndPoints = {{0, 0, 0xFF}, {0, 0, 0xFF}}; + // Hold a err ref to lowest quality compression, to check if new compression is any better + CGU_Vec2ui Q1CompData = cmpBlock; + // High Quality + CGU_UINT32 i; + + ALIGN_16 CGU_FLOAT Rpt[BLOCK_SIZE_4X4]; + CGU_UINT32 pcIndices = 0; + + m_nRefinementSteps = 0; + + CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X4]; // Normalized Block Input (0..1) in BGR channel format + // Default inidices & endpoints for Transparent Block + CGU_Vec3ui nEndpoints0 = {0, 0, 0}; // Endpoints are stored BGR as x,y,z + CGU_Vec3ui nEndpoints1 = {0xFF, 0xFF, 0xFF}; // Endpoints are stored BGR as x,y,z + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + Rpt[i] = 0.0f; + } + + //=============================================================== + // Check if we have more then 2 colors and process Alpha block + CGU_UINT32 dwColors = 0; + CGU_UINT32 dwBlk[BLOCK_SIZE_4X4]; + CGU_UINT32 R, G, B, A; + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + // Do any color conversion prior to processing the block + rgbBlock_normal[i] = isSRGB ? cmp_linearToSrgb(rgbBlockUVf[i]) : rgbBlockUVf[i]; + + R = (CGU_UINT32)(rgbBlock_normal[i].x * 255.0f); + G = (CGU_UINT32)(rgbBlock_normal[i].y * 255.0f); + B = (CGU_UINT32)(rgbBlock_normal[i].z * 255.0f); + + if (dwAlphaThreshold > 0) + A = (CGU_UINT32)BlockA[i]; + else + A = 255; + + // Punch Through Alpha in BC1 Codec (1 bit alpha) + if ((dwAlphaThreshold == 0) || (A >= dwAlphaThreshold)) + { + // copy to local RGB data and have alpha set to 0xFF + dwBlk[dwColors++] = A << 24 | R << 16 | G << 8 | B; + } + } + + if (!dwColors) + { + // All are colors transparent + EndPoints.Color0.x = EndPoints.Color0.y = EndPoints.Color0.z = 0.0f; + EndPoints.Color1.x = EndPoints.Color1.y = EndPoints.Color0.z = 255.0f; + nCmpIndices = 0xFFFFFFFF; + } + else + { + // We have colors to process + nCmpIndices = 0; +// Punch Through Alpha Support ToDo +// CGU_BOOL bHasAlpha = (dwColors != BLOCK_SIZE_4X4); +// bHasAlpha = bHasAlpha && (dwAlphaThreshold > 0); // valid for (dwNumChannels=4); +// if (bHasAlpha) { +// CGU_Vec2ui compBlock = {0xf800f800,0}; +// return compBlock; +// } + +// Here we are computing an unique number of sorted colors. +// For each unique value we compute the number of it appearences. +// qsort((void *)dwBlk, (size_t)dwColors, sizeof(CGU_UINT32), QSortIntCmp); +#ifndef ASPM_GPU + std::sort(dwBlk, dwBlk + 15); +#else + { + CGU_UINT32 j; + CMP_di what[BLOCK_SIZE_4X4]; + + for (i = 0; i < dwColors; i++) + { + what[i].index = i; + what[i].data = dwBlk[i]; + } + + CGU_UINT32 tmp_index; + CGU_UINT32 tmp_data; + + for (i = 1; i < dwColors; i++) + { + for (j = i; j > 0; j--) + { + if (what[j - 1].data > what[j].data) + { + tmp_index = what[j].index; + tmp_data = what[j].data; + what[j].index = what[j - 1].index; + what[j].data = what[j - 1].data; + what[j - 1].index = tmp_index; + what[j - 1].data = tmp_data; + } + } + } + for (i = 0; i < dwColors; i++) + dwBlk[i] = what[i].data; + } +#endif + CGU_UINT32 new_p; + CGU_UINT32 dwBlkU[BLOCK_SIZE_4X4]; + CGU_UINT32 dwUniqueColors = 0; + new_p = dwBlkU[0] = dwBlk[0]; + Rpt[dwUniqueColors] = 1.f; + for (i = 1; i < dwColors; i++) + { + if (new_p != dwBlk[i]) + { + dwUniqueColors++; + new_p = dwBlkU[dwUniqueColors] = dwBlk[i]; + Rpt[dwUniqueColors] = 1.f; + } + else + Rpt[dwUniqueColors] += 1.f; + } + dwUniqueColors++; + + // Simple case of only 2 colors to process + // no need for futher processing as lowest quality methods work best for this case + if (dwUniqueColors <= 2) + { + return Q1CompData; + } + else + { + // switch from int range back to UV floats + for (i = 0; i < dwUniqueColors; i++) + { + R = (dwBlkU[i] >> 16) & 0xff; + G = (dwBlkU[i] >> 8) & 0xff; + B = (dwBlkU[i] >> 0) & 0xff; + BlkInBGRf_UV[i].z = (CGU_FLOAT)R / 255.0f; + BlkInBGRf_UV[i].y = (CGU_FLOAT)G / 255.0f; + BlkInBGRf_UV[i].x = (CGU_FLOAT)B / 255.0f; + } + + CGU_Vec3f channelWeightsBGR; + channelWeightsBGR.x = channelWeights.z; + channelWeightsBGR.y = channelWeights.y; + channelWeightsBGR.z = channelWeights.x; + + EndPoints = CompressRGBBlock_Slow(BlkInBGRf_UV, Rpt, dwUniqueColors, channelWeightsBGR, m_nRefinementSteps); + } + } // colors + + //=================================================================== + // Process Cluster INPUT is constant EndPointsf OUTPUT is pcIndices + //=================================================================== + if (nCmpIndices == 0) + { + R = (CGU_UINT32)(EndPoints.Color0.z); + G = (CGU_UINT32)(EndPoints.Color0.y); + B = (CGU_UINT32)(EndPoints.Color0.x); + CGU_INT32 cluster0 = cmp_constructColor(R, G, B); + + R = (CGU_UINT32)(EndPoints.Color1.z); + G = (CGU_UINT32)(EndPoints.Color1.y); + B = (CGU_UINT32)(EndPoints.Color1.x); + CGU_INT32 cluster1 = cmp_constructColor(R, G, B); + + CGU_Vec3f InpRmp[NUM_ENDPOINTS]; + if ((cluster0 <= cluster1) // valid for 4 channels + // || (cluster0 > cluster1) // valid for 3 channels + ) + { + // inverse endpoints + InpRmp[0] = EndPoints.Color1; + InpRmp[1] = EndPoints.Color0; + } + else + { + InpRmp[0] = EndPoints.Color0; + InpRmp[1] = EndPoints.Color1; + } + + CGU_Vec3f srcblockBGR[BLOCK_SIZE_4X4]; + CGU_FLOAT srcblockA[BLOCK_SIZE_4X4]; + + // Swizzle the source RGB to BGR for processing + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + srcblockBGR[i].z = rgbBlock_normal[i].x * 255.0f; + srcblockBGR[i].y = rgbBlock_normal[i].y * 255.0f; + srcblockBGR[i].x = rgbBlock_normal[i].z * 255.0f; + srcblockA[i] = 0.0f; + if (dwAlphaThreshold > 0) + { + CGU_UINT32 alpha = (CGU_UINT32)BlockA[i]; + if (alpha >= dwAlphaThreshold) + srcblockA[i] = BlockA[i]; + } + } + + // input ramp is on the coarse grid + // make ramp endpoints the way they'll going to be decompressed + CGU_Vec3f InpRmpL[NUM_ENDPOINTS]; + CGU_Vec3f Fctrs = {32.0F, 64.0F, 32.0F}; // 1 << RG,1 << GG,1 << BG + + { + // ConstantRamp = MkWkRmpPts(InpRmpL, InpRmp); + InpRmpL[0] = InpRmp[0] + floor(InpRmp[0] / Fctrs); + InpRmpL[0] = cmp_clampVec3f(InpRmpL[0], 0.0f, 255.0f); + InpRmpL[1] = InpRmp[1] + floor(InpRmp[1] / Fctrs); + InpRmpL[1] = cmp_clampVec3f(InpRmpL[1], 0.0f, 255.0f); + } // MkWkRmpPts + + // build ramp + CGU_Vec3f LerpRmp[4]; + CGU_Vec3f offset = {1.0f, 1.0f, 1.0f}; + { + //BldRmp(Rmp, InpRmpL, dwNumChannels); + // linear interpolate end points to get the ramp + LerpRmp[0] = InpRmpL[0]; + LerpRmp[3] = InpRmpL[1]; + LerpRmp[1] = floor((InpRmpL[0] * 2.0f + LerpRmp[3] + offset) / 3.0f); + LerpRmp[2] = floor((InpRmpL[0] + LerpRmp[3] * 2.0f + offset) / 3.0f); + } // BldRmp + + //========================================================================= + // Clusterize, Compute error and find DXTC indexes for the current cluster + //========================================================================= + { + // Clusterize + CGU_UINT32 alpha; + + // For each colour in the original block assign it + // to the closest cluster and compute the cumulative error + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + alpha = (CGU_UINT32)srcblockA[i]; + if ((dwAlphaThreshold > 0) && alpha == 0) + { //*((CGU_DWORD *)&_Blk[i][AC]) == 0) + pcIndices |= cmp_set2Bit32(4, i); // dwNumChannels 3 or 4 (default is 4) + } + else + { + CGU_FLOAT shortest = 99999999999.f; + CGU_UINT8 shortestIndex = 0; + + CGU_Vec3f channelWeightsBGR; + channelWeightsBGR.x = channelWeights.z; + channelWeightsBGR.y = channelWeights.y; + channelWeightsBGR.z = channelWeights.x; + + for (CGU_UINT8 rampindex = 0; rampindex < 4; rampindex++) + { + // r is either 1 or 4 + // calculate the distance for each component + CGU_FLOAT distance = + dot(((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR), ((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR)); + if (distance < shortest) + { + shortest = distance; + shortestIndex = rampindex; + } + } + + // The total is a sum of (error += shortest) + // We have the index of the best cluster, so assign this in the block + // Reorder indices to match correct DXTC ordering + if (shortestIndex == 3) // dwNumChannels - 1 + shortestIndex = 1; + else if (shortestIndex) + shortestIndex++; + pcIndices |= cmp_set2Bit32(shortestIndex, i); + } + } // BLOCK_SIZE_4X4 + } // Clusterize + } // Process Cluster + + //============================================================== + // Generate Compressed Result from nEndpoints & pcIndices + //============================================================== + R = (CGU_UINT32)(EndPoints.Color0.z); + G = (CGU_UINT32)(EndPoints.Color0.y); + B = (CGU_UINT32)(EndPoints.Color0.x); + c0 = cmp_constructColor(R, G, B); + + R = (CGU_UINT32)(EndPoints.Color1.z); + G = (CGU_UINT32)(EndPoints.Color1.y); + B = (CGU_UINT32)(EndPoints.Color1.x); + c1 = cmp_constructColor(R, G, B); + + // Get Processed indices if not set + if (nCmpIndices == 0) + nCmpIndices = pcIndices; + + if (c0 <= c1) + { + cmpBlock.x = c1 | (c0 << 16); + } + else + cmpBlock.x = c0 | (c1 << 16); + + cmpBlock.y = nCmpIndices; + + // Select best compression + CGU_FLOAT CompErr = CMP_RGBBlockError(rgbBlockUVf, cmpBlock, isSRGB); + if (CompErr > errLQ) + cmpBlock = Q1CompData; + } +#endif + return cmpBlock; +} + +//============================= Alpha: New single header interfaces: supports GPU shader interface ================================================== + +// Compress a BC1 block - Use new code in cmp_bc1.h +static CGU_Vec2ui CompressBlockBC1_UNORM(CGU_Vec3f rgbablockf[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB) +{ + CGU_FLOAT BlockA[BLOCK_SIZE_4X4]; // Not used but required + CGU_Vec3f channelWeights = {1.0f, 1.0f, 1.0f}; + + return CompressBlockBC1_RGBA_Internal(rgbablockf, + BlockA, // ToDo support nullptr + channelWeights, + 0, + 1, + fquality, + isSRGB); +} + +// Compress a BC2 block +static CGU_Vec4ui CompressBlockBC2_UNORM(CMP_IN CGU_Vec3f BlockRGB[BLOCK_SIZE_4X4], + CMP_IN CGU_FLOAT BlockA[BLOCK_SIZE_4X4], + CGU_FLOAT fquality, + CGU_BOOL isSRGB) +{ + CGU_Vec2ui compressedBlocks; + CGU_Vec4ui compBlock; + compressedBlocks = cmp_compressExplicitAlphaBlock(BlockA); + compBlock.x = compressedBlocks.x; + compBlock.y = compressedBlocks.y; + + CGU_Vec3f channelWeights = {1.0f, 1.0f, 1.0f}; + compressedBlocks = CompressBlockBC1_RGBA_Internal(BlockRGB, BlockA, channelWeights, 0, 1, fquality, isSRGB); + compBlock.z = compressedBlocks.x; + compBlock.w = compressedBlocks.y; + return compBlock; +} + +// Compress a BC3 block +static CGU_Vec4ui CompressBlockBC3_UNORM(CMP_IN CGU_Vec3f BlockRGB[BLOCK_SIZE_4X4], + CMP_IN CGU_FLOAT BlockA[BLOCK_SIZE_4X4], + CGU_FLOAT fquality, + CGU_BOOL isSRGB) +{ + CGU_Vec4ui compBlock; + CGU_Vec2ui cmpBlock; + + cmpBlock = cmp_compressAlphaBlock(BlockA, fquality, FALSE); + compBlock.x = cmpBlock.x; + compBlock.y = cmpBlock.y; + + CGU_Vec2ui compressedBlocks; + compressedBlocks = CompressBlockBC1_UNORM(BlockRGB, fquality, isSRGB); + compBlock.z = compressedBlocks.x; + compBlock.w = compressedBlocks.y; + return compBlock; +} + +// Compress a BC4 block +static CGU_Vec2ui CompressBlockBC4_UNORM(CMP_IN CGU_FLOAT Block[BLOCK_SIZE_4X4], CGU_FLOAT fquality) +{ + CGU_Vec2ui cmpBlock; + cmpBlock = cmp_compressAlphaBlock(Block, fquality, FALSE); + return cmpBlock; +} + +// Compress a BC4 block +static CGU_Vec2ui CompressBlockBC4_SNORM(CMP_IN CGU_FLOAT Block[BLOCK_SIZE_4X4], CGU_FLOAT fquality) +{ + CGU_Vec2ui cmpBlock; + cmpBlock = cmp_compressAlphaBlock(Block, fquality, TRUE); + return cmpBlock; +} + +// Compress a BC5 block +static CGU_Vec4ui CompressBlockBC5_UNORM(CMP_IN CGU_FLOAT BlockU[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT BlockV[BLOCK_SIZE_4X4], CGU_FLOAT fquality) +{ + CGU_Vec4ui compressedBlock = {0, 0, 0, 0}; + CGU_Vec2ui cmpBlock; + cmpBlock = cmp_compressAlphaBlock(BlockU, fquality, FALSE); + compressedBlock.x = cmpBlock.x; + compressedBlock.y = cmpBlock.y; + + cmpBlock = cmp_compressAlphaBlock(BlockV, fquality, FALSE); + compressedBlock.z = cmpBlock.x; + compressedBlock.w = cmpBlock.y; + + return compressedBlock; +} + +// Compress a BC6 & BC7 UNORM block ToDo + +#endif diff --git a/WickedEngine/shaders/compressonator/common_def.h b/WickedEngine/shaders/compressonator/common_def.h new file mode 100644 index 000000000..aac8f7d22 --- /dev/null +++ b/WickedEngine/shaders/compressonator/common_def.h @@ -0,0 +1,2296 @@ +// ================================================================================================== +// Copyright (c) 2007-2021 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +// Copyright (c) <2014> +// ================================================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// +// File Name: Common_Def +// Description: common definitions used for CPU/HPC/GPU +// +// Pull changes: +// Fixed build of cmp_core on Mac. (#164) +// + +#ifndef CMP_COMMON_DEFINITIONS_H +#define CMP_COMMON_DEFINITIONS_H + +//#define USE_CMP_FIDELITY_FX_H + +// Proxy ISPC compiler (Warning! Not all ASPM features will be available : expect build errors for specialized ASPM code! +#ifdef ISPC +#define ASPM +#endif + +// Using OpenCL Compiler +#ifdef __OPENCL_VERSION__ +#define ASPM_OPENCL +#endif + +#ifdef USE_CMP_FIDELITY_FX_H + +// Fidelity FX SDK mapping +#if defined(A_CPU) +#ifndef ASPM_CPU +#define ASPM_CPU +#endif +#endif + +#if defined(A_GPU) +#ifndef ASPM_GPU +#define ASPM_GPU +#endif +#endif + +#if defined(A_GLS) +#ifndef ASPM_GLS +#define ASPM_GLS +#endif +#endif + +#if defined(A_HLSL) +#ifndef ASPM_HLSL +#define ASPM_HLSL +#endif +#endif + +#if defined(A_GCC) +#ifndef ASPM_GCC +#define ASPM_GCC +#endif +#endif + +#endif + +#if (defined(ASPM_HLSL) || defined(ASPM_OPENCL)) +#ifndef ASPM_GPU +#define ASPM_GPU +#endif +#endif + + +//======================================= +// Skip CMP if only using FidelityFX Code +//======================================= +#if !(defined(A_GPU) || defined(A_GLS) || defined(A_HLSL) || defined(A_GCC)) + +// The shaders for UE4 require extension in the form of .ush in place of standard .h +// this directive is used to make the change without users requiring to modify all of the include extensions +// specific to UE4 + +#ifdef ASPM_HLSL_UE4 +#pragma once +#define INC_cmp_math_vec4 "cmp_math_vec4.ush" +#define INC_cmp_math_func "cmp_math_func.ush" +#else +#define INC_cmp_math_vec4 "cmp_math_vec4.h" +#define INC_cmp_math_func "cmp_math_func.h" +#endif + +#if defined(__linux__) || defined(__unix__) || defined(__APPLE__) +#ifndef _LINUX +#define _LINUX +#endif +#ifdef ASPM_GPU +#undef ASPM_GPU +#endif +#include +#include +#include +#include INC_cmp_math_vec4 +#endif + +#ifdef _WIN32 +//#define USE_ASPM_CODE +#include +#endif + +#ifndef CMP_MAX +#define CMP_MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef CMP_MIN +#define CMP_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#ifndef cmp_isnan +#ifdef ASPM_GPU +#define cmp_isnan(x) isnan(x) +#else +#define cmp_isnan(x) std::isnan(x) +#endif +#endif + +#ifndef cmp_isinf +#ifdef ASPM_GPU +#define cmp_isinf(x) isinf(x) +#else +#define cmp_isinf(x) std::isinf(x) +#endif +#endif + +#ifdef ASPM_GPU +#define CMP_STATIC_CAST(x, y) (x)(y) +#define CMP_TYPE_CAST(x) (x) +#else +#define CMP_STATIC_CAST(x, y) static_cast(y) +#define CMP_TYPE_CAST(x) (x&) +#endif + +// Sets mapping BC1, BC2 & BC3 to decode Red,Green,Blue and Alpha +// RGBA to channels [0,1,2,3] else BGRA maps to [0,1,2,3] +// BC4 alpha always maps as AAAA to channels [0,1,2,3] +// BC5 decoded (Red&Green) maps R,G,B=0,A=255 to [0,1,2,3] else maps [B=0,G,R,A=255] to [0,1,2,3] +#define CMP_SET_BC13_DECODER_RGBA +#define CMP_FLOAT_MAX 3.402823466e+38F // max value used to detect an Error in processing +#define CMP_FLOAT_MAX_EXP 38 +#define USE_PROCESS_SEPERATE_ALPHA // Enable this to use higher quality code using CompressDualIndexBlock +#define COMPRESSED_BLOCK_SIZE 16 // Size of a compressed block in bytes +#define MAX_DIMENSION_BIG 4 // Max number of channels (RGBA) +#define MAX_SUBSETS 3 // Maximum number of possible subsets +#define MAX_SUBSET_SIZE 16 // Largest possible size for an individual subset +#define BLOCK_SIZE_4X4X4 64 +#define BLOCK_SIZE_4X4 16 +#define BlockX 4 +#define BlockY 4 +//#define USE_BLOCK_LINEAR // Source Data is organized in linear form for each block : Experimental Code not fully developed +//#define USE_DOUBLE // Default is to use float, enable to use double data types only for float definitions + +#ifdef ASPM +#define CMP_UNIFORM uniform +#define CMP_VARYING varying +#else +#define CMP_UNIFORM +#define CMP_VARYING +#endif +//--------------------------------------------- +// Predefinitions for GPU and CPU compiled code +//--------------------------------------------- +#ifdef ASPM_HLSL +// ==== Vectors ==== +typedef float2 CGU_Vec2f; +typedef float2 CGV_Vec2f; +typedef float3 CGU_Vec3f; +typedef float3 CGV_Vec3f; +typedef float4 CGU_Vec4f; +typedef float4 CGV_Vec4f; + +typedef int2 CGU_Vec2i; +typedef int2 CGV_Vec2i; +typedef uint2 CGU_Vec2ui; +typedef uint2 CGV_Vec2ui; + +typedef int3 CGU_Vec3i; +typedef int3 CGV_Vec3i; +typedef uint3 CGU_Vec3ui; +typedef uint3 CGV_Vec3ui; + +typedef int4 CGU_Vec4i; +typedef int4 CGV_Vec4i; + +typedef int4 CGU_Vec4uc; +typedef int4 CGV_Vec4uc; + +typedef uint4 CGU_Vec4ui; +typedef uint4 CGV_Vec4ui; + +// ==== Scalar Types ==== to remove from code +typedef int CGU_INT8; +typedef int CGU_INT; +typedef int CGV_INT; +typedef uint CGU_UINT8; +typedef uint CGU_UINT; + +// ==== Scalar Types ==== +typedef int CGU_BOOL; +typedef int CGV_BOOL; +typedef int CGV_INT8; +typedef int CGV_UINT8; +typedef uint CGU_UINT16; +typedef int CGU_INT32; +typedef int CGV_INT32; +typedef uint CGU_UINT32; +typedef uint CGV_UINT32; +typedef float CGV_FLOAT; +typedef float CGU_FLOAT; +typedef min16float CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support + +#define TRUE 1 +#define FALSE 0 +#define CMP_CDECL + +#define BC7_ENCODECLASS +#define CMP_EXPORT +#define INLINE inline +#define CMP_UNIFORM +#define varying +#define CMP_GLOBAL +#define CMP_KERNEL +#define CMP_CONSTANT const +#define CMP_STATIC static +#define CMP_REFINOUT +#define CMP_PTRINOUT +#define CMP_INOUT inout +#define CMP_OUT out +#define CMP_IN in +#define CMP_UNUSED(x) (x); +#define CMP_UNROLL [unroll] +#define CMP_SVGROUPINDEX :SV_GroupIndex +#define CMP_SVGROUPID :SV_GroupID +#define CMP_NUMTHREADS(x, y, z) [numthreads(x, y, z)] + +#else + +#define CMP_SVGROUPINDEX +#define CMP_SVGROUPID +#define CMP_NUMTHREADS(x, y, z) + +typedef enum +{ + CGU_CORE_OK = 0, // No errors, call was successfull + CGU_CORE_ERR_UNKOWN, // An unknown error occurred + CGU_CORE_ERR_NEWMEM, // New Memory Allocation Failed + CGU_CORE_ERR_INVALIDPTR, // The pointer value used is invalid or null + CGU_CORE_ERR_RANGERED, // values for Red Channel is out of range (too high or too low) + CGU_CORE_ERR_RANGEGREEN, // values for Green Channel is out of range (too high or too low) + CGU_CORE_ERR_RANGEBLUE, // values for Blue Channel is out of range (too high or too low) +} CGU_ERROR_CODES; + +#ifdef ASPM_OPENCL // GPU Based code using OpenCL +// ==== Vectors ==== +typedef float2 CGU_Vec2f; +typedef float2 CGV_Vec2f; +typedef float3 CMP_Vec3f; +typedef float3 CGU_Vec3f; +typedef float3 CGV_Vec3f; +typedef float4 CGU_Vec4f; +typedef float4 CGV_Vec4f; + +typedef uchar3 CGU_Vec3uc; +typedef uchar3 CGV_Vec3uc; + +typedef uchar4 CMP_Vec4uc; +typedef uchar4 CGU_Vec4uc; +typedef uchar4 CGV_Vec4uc; + +typedef int2 CGU_Vec2i; +typedef int2 CGV_Vec2i; +typedef int3 CGU_Vec3i; +typedef int3 CGV_Vec3i; +typedef int4 CGU_Vec4i; +typedef int4 CGV_Vec4i; + + +typedef uint2 CGU_Vec2ui; +typedef uint2 CGV_Vec2ui; +typedef uint3 CGU_Vec3ui; +typedef uint3 CGV_Vec3ui; +typedef uint4 CGU_Vec4ui; +typedef uint4 CGV_Vec4ui; + +#define USE_BC7_SP_ERR_IDX +#define BC7_ENCODECLASS +#define ASPM_PRINT(args) printf args + +#define CMP_EXPORT +#define INLINE +#define CMP_UNIFORM +#define varying +#define CMP_GLOBAL __global +#define CMP_KERNEL __kernel +#define CMP_CONSTANT __constant +#define CMP_STATIC +#define CMP_REFINOUT & +#define CMP_PTRINOUT * +#define CMP_INOUT +#define CMP_OUT +#define CMP_IN +#define CMP_UNUSED(x) +#define CMP_UNROLL + +typedef unsigned int CGU_DWORD; //32bits +typedef int CGU_INT; //32bits +typedef bool CGU_BOOL; +typedef unsigned short CGU_SHORT; //16bits +typedef float CGU_FLOAT; +typedef half CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support +typedef unsigned int uint32; // need to remove this def + +typedef int CGV_INT; +typedef unsigned int CGU_UINT; +typedef int CGUV_INT; +typedef int CGV_BOOL; + +typedef char CGU_INT8; +typedef unsigned char CGU_UINT8; +typedef short CGU_INT16; +typedef unsigned short CGU_UINT16; +typedef int CGU_INT32; +typedef unsigned int CGU_UINT32; +typedef unsigned long long CGU_UINT64; + +typedef char CGV_INT8; +typedef unsigned char CGV_UINT8; +typedef short CGV_INT16; +typedef unsigned short CGV_UINT16; +typedef int CGV_INT32; +typedef unsigned int CGV_UINT32; +typedef unsigned long CGV_UINT64; + +typedef float CGV_FLOAT; + +#define TRUE 1 +#define FALSE 0 +#define CMP_CDECL + +#else +// CPU & ASPM definitions + +#define CMP_REFINOUT & +#define CMP_PTRINOUT * +#define CMP_INOUT +#define CMP_OUT +#define CMP_IN +#define CMP_UNUSED(x) (void)(x); +#define CMP_UNROLL + +#ifdef ASPM // SPMD ,SIMD CPU code +// using hybrid (CPU/GPU) aspm compiler +#define ASPM_PRINT(args) print args +#define CMP_USE_FOREACH_ASPM +#define __ASPM__ +#define BC7_ENCODECLASS + +#define USE_BC7_SP_ERR_IDX +//#define USE_BC7_RAMP + +#define CMP_EXPORT export +#define TRUE true +#define FALSE false +typedef CMP_UNIFORM bool CGU_BOOL; +typedef bool CGV_BOOL; + +typedef unsigned int8 uint8; +typedef unsigned int16 uint16; +typedef unsigned int32 uint32; +typedef unsigned int64 uint64; +typedef CMP_UNIFORM float CGU_FLOAT; +typedef varying float CGV_FLOAT; +typedef CMP_UNIFORM float CGU_MIN16_FLOAT; + +typedef CMP_UNIFORM uint16 CGU_UINT16; + +typedef CMP_UNIFORM uint8 CGU_UINT8; +typedef varying uint8 CGV_UINT8; + +typedef CMP_UNIFORM uint64 CGU_UINT64; + +typedef CGV_UINT8<4> CGV_Vec4uc; +typedef CGU_UINT8<4> CGU_Vec4uc; + +typedef CGU_FLOAT<2> CGU_Vec2f; +typedef CGV_FLOAT<2> CGV_Vec2f; +typedef CGU_FLOAT<3> CGU_Vec3f; +typedef CGV_FLOAT<3> CGV_Vec3f; +typedef CGU_FLOAT<4> CGU_Vec4f; +typedef CGV_FLOAT<4> CGV_Vec4f; + +typedef CGU_UINT32<3> CGU_Vec3ui; +typedef CGV_UINT32<3> CGV_Vec3ui; + +typedef CGU_UINT32<4> CGU_Vec4ui; +typedef CGV_UINT32<4> CGV_Vec4ui; + +#define CMP_CDECL +#else // standard CPU code +#include +#include +#include INC_cmp_math_vec4 + +// using CPU compiler +#define ASPM_PRINT(args) printf args +#define USE_BC7_RAMP +#define USE_BC7_SP_ERR_IDX + +#define CMP_EXPORT +#define BC7_ENCODECLASS BC7_EncodeClass:: +#define TRUE 1 +#define FALSE 0 +#define CMP_UNIFORM +#define varying + +typedef char int8; +typedef short int16; +typedef int int32; +typedef long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef int8 CGV_BOOL; +typedef bool CGU_BOOL; +typedef int16 CGU_WORD; +typedef uint8 CGU_SHORT; +typedef long CGU_LONG; +typedef unsigned long CGU_ULONG; + +typedef CMP_UNIFORM float CGU_FLOAT; +typedef varying float CGV_FLOAT; +typedef CMP_UNIFORM float CGU_MIN16_FLOAT; + +typedef CMP_UNIFORM uint8 CGU_UINT8; +typedef varying uint8 CGV_UINT8; + +typedef CMP_UNIFORM uint16 CGU_UINT16; + +typedef CMP_Vec3ui CGU_Vec3ui; +typedef CMP_Vec3ui CGV_Vec3ui; + +typedef CMP_Vec2f CGU_Vec2f; +typedef CMP_Vec3f CGU_Vec3f; + +typedef CMP_Vec4uc CGU_Vec4uc; +typedef CMP_Vec4uc CGV_Vec4uc; +typedef CMP_Vec4i CGU_Vec4i; +typedef CMP_Vec4ui CGU_Vec4ui; +typedef CMP_Vec4ui CGV_Vec4ui; +typedef CMP_Vec4f CGU_Vec4f; +typedef CMP_Vec4f CGV_Vec4f; + +#if defined(WIN32) || defined(_WIN64) +#define CMP_CDECL __cdecl +#else +#define CMP_CDECL +#endif +#endif + +// Common CPU & ASPM definitions +#define CMP_ASSERT(arg) + +#define CMP_GLOBAL + +#define CMP_KERNEL +#define __local const +#define __constant const +#define CMP_CONSTANT const +#define INLINE inline +#define CMP_STATIC static + +typedef CMP_UNIFORM int32 CGU_DWORD; +typedef CMP_UNIFORM uint8 CGU_UBYTE; +typedef CMP_UNIFORM int CGU_INT; +typedef CMP_UNIFORM int8 CGU_INT8; + +typedef CMP_UNIFORM int16 CGU_INT16; +typedef CMP_UNIFORM uint16 CGU_UINT16; +typedef CMP_UNIFORM int32 CGU_INT32; +typedef CMP_UNIFORM uint32 CGU_UINT32; +typedef CMP_UNIFORM uint64 CGU_UINT64; + +typedef int CGV_INT; +typedef int8 CGV_INT8; +typedef int16 CGV_INT16; +typedef int32 CGV_INT32; +typedef uint16 CGV_UINT16; +typedef uint32 CGV_UINT32; +typedef uint64 CGV_UINT64; + +#endif // else ASPM_GPU + + + +typedef struct +{ + CGU_UINT32 m_src_width; + CGU_UINT32 m_src_height; + CGU_UINT32 m_width_in_blocks; + CGU_UINT32 m_height_in_blocks; + CGU_FLOAT m_fquality; +} Source_Info; + +typedef unsigned char* CGU_PTR; + +// Ref Compute_CPU_HPC +struct texture_surface +{ + CGU_PTR ptr; + CGU_INT width, height, stride; + CGU_INT channels; +}; +#endif // else ASPM_HLSL + +#endif // USE CMP defines + +//======================================= +// using FidelityFX Code +//======================================= +#ifdef USE_CMP_FIDELITY_FX_H +// ================================================================================================== +// [A] SHADER PORTABILITY 1.20190530 +// ================================================================================================== +// ABOUT +// ===== +// Common central point for high-level shading language and C portability for various shader headers. +//--------------------------------------------------------------------------------------------------- +// DEFINES TYPES FOR CMP AND FIDELITY FX +// Both ASPM_ and A_ types are interchanable in code, the table below show compatibility between types +// ================================================================================================== +// Compressonator Fidelity FX Comments +// ASPM_CPU A_CPU Include the CPU related code. +// ASPM_GPU A_GPU Include the GPU related code. +// ASPM_GLSL A_GLS Using GLSL. +// ASMP_HLSL A_HLS Using HLSL. +// - A_GCC Using a GCC compatible compiler (else assume MSVC compatible compiler by default). +// ======= +// CGU_UINT8 A_BYTE Support 8-bit integer. +// CGU_HALF A_HALF Support 16-bit integer and floating point. +// A_LONG Support 64-bit integer. (GLSL & CPU only) +// A_DUBL Support 64-bit floating point. +// ======= +// A_WAVE Support wave-wide operations. +//-------------------------------------------------------------------------------------------------- +// To get #include "common_def.h" working in GLSL use '#extension GL_GOOGLE_include_directive:require' +//--------------------------------------------------------------------------------------------------- +// SIMPLIFIED TYPE SYSTEM FOR FX CODE +// ================================================================================================== +// - All ints will be unsigned with exception of when signed is required. +// - Type naming simplified and shortened "A<#components>", +// - H = 16-bit float (half) +// - F = 32-bit float (float) +// - D = 64-bit float (double) +// - P = 1-bit integer (predicate, not using bool because 'B' is used for byte) +// - B = 8-bit integer (byte) +// - W = 16-bit integer (word) +// - U = 32-bit integer (unsigned) +// - L = 64-bit integer (long) +// - Using "AS<#components>" for signed when required. +//--------------------------------------------------------------------------------------------------- +// TODO +// ==== +// - Make sure 'ALerp*(a,b,m)' does 'b*m+(-a*m+a)' (2 ops). +// - Add subgroup ops. +//------------------------------------------------------------------------------------------------------------------------------ +// CHANGE LOG +// ========== +// 20210518 - Merged CMP Common_def.h with ffx_a.h +//============================================================================================================================== + + +#define A_2PI 6.28318530718 // 2xPI + +//============================================================================================================================== +// CPU +//============================================================================================================================== +// This provides a minimum subset of functionality compared to the GPU parts. +//============================================================================================================================== +#if defined(A_CPU) || !(defined(ASPM_GPU) || defined(ASPM_HLSL) || defined(ASPM_OPENCL)) + +#include "stdint.h" + +// Supporting user defined overrides. +#ifndef A_RESTRICT +#define A_RESTRICT __restrict // CMP_RESTRICT +#endif +//------------------------------------------------------------------------------------------------------------------------------ +#ifndef A_STATIC +#define A_STATIC static // CMP_STATIC +#endif + //------------------------------------------------------------------------------------------------------------------------------ + // Same types across CPU and GPU. + // Predicate uses 32-bit integer (C friendly bool). + typedef uint32_t AP1; // CGU_UINT32 + typedef float AF1; // CGU_FLOAT + typedef double AD1; // CGU_DOUBLE + typedef uint8_t AB1; // CGU_UINT8 + typedef uint16_t AW1; // CGU_UINT16 + typedef uint32_t AU1; // CGU_UINT32 + typedef uint64_t AL1; // CGU_UINT64 + typedef int8_t ASB1; // CGU_INT8 + typedef int16_t ASW1; // CGU_INT16 + typedef int32_t ASU1; // CGU_INT32 + typedef int64_t ASL1; // CGU_INT64 + +//------------------------------------------------------------------------------------------------------------------------------ + #define AD1_(a) ((AD1)(a)) + #define AF1_(a) ((AF1)(a)) + #define AL1_(a) ((AL1)(a)) + #define AU1_(a) ((AU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1_(a) ((ASL1)(a)) + #define ASU1_(a) ((ASU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AU1 AU1_AF1(AF1 a){union{AF1 f;AU1 u;}bits;bits.f=a;return bits.u;} +//------------------------------------------------------------------------------------------------------------------------------ + #define A_TRUE 1 + #define A_FALSE 0 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// CPU/GPU PORTING +// +//------------------------------------------------------------------------------------------------------------------------------ +// Hackary to get CPU and GPU to share all setup code, without duplicate code paths. +// Unfortunately this is the level of "ugly" that is required since the languages are very different. +// This uses a lower-case prefix for special vector constructs. +// - In C restrict pointers are used. +// - In the shading language, in/inout/out arguments are used. +// This depends on the ability to access a vector value in both languages via array syntax (aka color[2]). +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD1 *A_RESTRICT + #define retAD3 AD1 *A_RESTRICT + #define retAD4 AD1 *A_RESTRICT + #define retAF2 AF1 *A_RESTRICT + #define retAF3 AF1 *A_RESTRICT + #define retAF4 AF1 *A_RESTRICT + #define retAL2 AL1 *A_RESTRICT + #define retAL3 AL1 *A_RESTRICT + #define retAL4 AL1 *A_RESTRICT + #define retAU2 AU1 *A_RESTRICT + #define retAU3 AU1 *A_RESTRICT + #define retAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 AD1 *A_RESTRICT + #define inAD3 AD1 *A_RESTRICT + #define inAD4 AD1 *A_RESTRICT + #define inAF2 AF1 *A_RESTRICT + #define inAF3 AF1 *A_RESTRICT + #define inAF4 AF1 *A_RESTRICT + #define inAL2 AL1 *A_RESTRICT + #define inAL3 AL1 *A_RESTRICT + #define inAL4 AL1 *A_RESTRICT + #define inAU2 AU1 *A_RESTRICT + #define inAU3 AU1 *A_RESTRICT + #define inAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 AD1 *A_RESTRICT + #define inoutAD3 AD1 *A_RESTRICT + #define inoutAD4 AD1 *A_RESTRICT + #define inoutAF2 AF1 *A_RESTRICT + #define inoutAF3 AF1 *A_RESTRICT + #define inoutAF4 AF1 *A_RESTRICT + #define inoutAL2 AL1 *A_RESTRICT + #define inoutAL3 AL1 *A_RESTRICT + #define inoutAL4 AL1 *A_RESTRICT + #define inoutAU2 AU1 *A_RESTRICT + #define inoutAU3 AU1 *A_RESTRICT + #define inoutAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 AD1 *A_RESTRICT + #define outAD3 AD1 *A_RESTRICT + #define outAD4 AD1 *A_RESTRICT + #define outAF2 AF1 *A_RESTRICT + #define outAF3 AF1 *A_RESTRICT + #define outAF4 AF1 *A_RESTRICT + #define outAL2 AL1 *A_RESTRICT + #define outAL3 AL1 *A_RESTRICT + #define outAL4 AL1 *A_RESTRICT + #define outAU2 AU1 *A_RESTRICT + #define outAU3 AU1 *A_RESTRICT + #define outAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD1 x[2] + #define varAD3(x) AD1 x[3] + #define varAD4(x) AD1 x[4] + #define varAF2(x) AF1 x[2] + #define varAF3(x) AF1 x[3] + #define varAF4(x) AF1 x[4] + #define varAL2(x) AL1 x[2] + #define varAL3(x) AL1 x[3] + #define varAL4(x) AL1 x[4] + #define varAU2(x) AU1 x[2] + #define varAU3(x) AU1 x[3] + #define varAU4(x) AU1 x[4] +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) {x,y} + #define initAD3(x,y,z) {x,y,z} + #define initAD4(x,y,z,w) {x,y,z,w} + #define initAF2(x,y) {x,y} + #define initAF3(x,y,z) {x,y,z} + #define initAF4(x,y,z,w) {x,y,z,w} + #define initAL2(x,y) {x,y} + #define initAL3(x,y,z) {x,y,z} + #define initAL4(x,y,z,w) {x,y,z,w} + #define initAU2(x,y) {x,y} + #define initAU3(x,y,z) {x,y,z} + #define initAU4(x,y,z,w) {x,y,z,w} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Replace transcendentals with manual versions. +//============================================================================================================================== + #ifdef A_GCC + A_STATIC AD1 AAbsD1(AD1 a){return __builtin_fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return __builtin_fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(__builtin_abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(__builtin_labs(ASL1_(a)));} + #else + A_STATIC AD1 AAbsD1(AD1 a){return fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(llabs(ASL1_(a)));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ACosD1(AD1 a){return __builtin_cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return __builtin_cosf(a);} + #else + A_STATIC AD1 ACosD1(AD1 a){return cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return cosf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ADotD2(inAD2 a,inAD2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AD1 ADotD3(inAD3 a,inAD3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AD1 ADotD4(inAD4 a,inAD4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} + A_STATIC AF1 ADotF2(inAF2 a,inAF2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AF1 ADotF3(inAF3 a,inAF3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AF1 ADotF4(inAF4 a,inAF4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AExp2D1(AD1 a){return __builtin_exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return __builtin_exp2f(a);} + #else + A_STATIC AD1 AExp2D1(AD1 a){return exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return exp2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AFloorD1(AD1 a){return __builtin_floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return __builtin_floorf(a);} + #else + A_STATIC AD1 AFloorD1(AD1 a){return floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return floorf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ALerpD1(AD1 a,AD1 b,AD1 c){return b*c+(-a*c+a);} + A_STATIC AF1 ALerpF1(AF1 a,AF1 b,AF1 c){return b*c+(-a*c+a);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ALog2D1(AD1 a){return __builtin_log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return __builtin_log2f(a);} + #else + A_STATIC AD1 ALog2D1(AD1 a){return log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return log2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMaxD1(AD1 a,AD1 b){return a>b?a:b;} + A_STATIC AF1 AMaxF1(AF1 a,AF1 b){return a>b?a:b;} + A_STATIC AL1 AMaxL1(AL1 a,AL1 b){return a>b?a:b;} + A_STATIC AU1 AMaxU1(AU1 a,AU1 b){return a>b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + // These follow the convention that A integer types don't have signage, until they are operated on. + A_STATIC AL1 AMaxSL1(AL1 a,AL1 b){return (ASL1_(a)>ASL1_(b))?a:b;} + A_STATIC AU1 AMaxSU1(AU1 a,AU1 b){return (ASU1_(a)>ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMinD1(AD1 a,AD1 b){return a>ASL1_(b));} + A_STATIC AU1 AShrSU1(AU1 a,AU1 b){return AU1_(ASU1_(a)>>ASU1_(b));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASinD1(AD1 a){return __builtin_sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return __builtin_sinf(a);} + #else + A_STATIC AD1 ASinD1(AD1 a){return sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return sinf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASqrtD1(AD1 a){return __builtin_sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return __builtin_sqrtf(a);} + #else + A_STATIC AD1 ASqrtD1(AD1 a){return sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return sqrtf(a);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + A_STATIC AD1 AFractD1(AD1 a){return a-AFloorD1(a);} + A_STATIC AF1 AFractF1(AF1 a){return a-AFloorF1(a);} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 APowD1(AD1 a,AD1 b){return AExp2D1(b*ALog2D1(a));} + A_STATIC AF1 APowF1(AF1 a,AF1 b){return AExp2F1(b*ALog2F1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARsqD1(AD1 a){return ARcpD1(ASqrtD1(a));} + A_STATIC AF1 ARsqF1(AF1 a){return ARcpF1(ASqrtF1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ASatD1(AD1 a){return AMinD1(1.0,AMaxD1(0.0,a));} + A_STATIC AF1 ASatF1(AF1 a){return AMinF1(1.0f,AMaxF1(0.0f,a));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + A_STATIC retAD2 opAAbsD2(outAD2 d,inAD2 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);return d;} + A_STATIC retAD3 opAAbsD3(outAD3 d,inAD3 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);return d;} + A_STATIC retAD4 opAAbsD4(outAD4 d,inAD4 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);d[3]=AAbsD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAbsF2(outAF2 d,inAF2 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);return d;} + A_STATIC retAF3 opAAbsF3(outAF3 d,inAF3 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);return d;} + A_STATIC retAF4 opAAbsF4(outAF4 d,inAF4 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);d[3]=AAbsF1(a[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opACpyD2(outAD2 d,inAD2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAD3 opACpyD3(outAD3 d,inAD3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAD4 opACpyD4(outAD4 d,inAD4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opACpyF2(outAF2 d,inAF2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAF3 opACpyF3(outAF3 d,inAF3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAF4 opACpyF4(outAF4 d,inAF4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);return d;} + A_STATIC retAD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);return d;} + A_STATIC retAD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);d[3]=ALerpD1(a[3],b[3],c[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);return d;} + A_STATIC retAF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);return d;} + A_STATIC retAF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);d[3]=ALerpF1(a[3],b[3],c[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);return d;} + A_STATIC retAD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);return d;} + A_STATIC retAD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);d[3]=ALerpD1(a[3],b[3],c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);return d;} + A_STATIC retAF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);return d;} + A_STATIC retAF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);d[3]=ALerpF1(a[3],b[3],c);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);d[3]=AMaxD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);d[3]=AMaxF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);d[3]=AMinD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);d[3]=AMinF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opANegD2(outAD2 d,inAD2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAD3 opANegD3(outAD3 d,inAD3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAD4 opANegD4(outAD4 d,inAD4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opANegF2(outAF2 d,inAF2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAF3 opANegF3(outAF3 d,inAF3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAF4 opANegF4(outAF4 d,inAF4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opARcpD2(outAD2 d,inAD2 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);return d;} + A_STATIC retAD3 opARcpD3(outAD3 d,inAD3 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);return d;} + A_STATIC retAD4 opARcpD4(outAD4 d,inAD4 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);d[3]=ARcpD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opARcpF2(outAF2 d,inAF2 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);return d;} + A_STATIC retAF3 opARcpF3(outAF3 d,inAF3 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);return d;} + A_STATIC retAF4 opARcpF4(outAF4 d,inAF4 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);d[3]=ARcpF1(a[3]);return d;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF FLOAT PACKING +//============================================================================================================================== + // Convert float to half (in lower 16-bits of output). + // Same fast technique as documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf + // Supports denormals. + // Conversion rules are to make computations possibly "safer" on the GPU, + // -INF & -NaN -> -65504 + // +INF & +NaN -> +65504 + A_STATIC AU1 AU1_AH1_AF1(AF1 f){ + static AW1 base[512]={ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100, + 0x0200,0x0400,0x0800,0x0c00,0x1000,0x1400,0x1800,0x1c00,0x2000,0x2400,0x2800,0x2c00,0x3000,0x3400,0x3800,0x3c00, + 0x4000,0x4400,0x4800,0x4c00,0x5000,0x5400,0x5800,0x5c00,0x6000,0x6400,0x6800,0x6c00,0x7000,0x7400,0x7800,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8001,0x8002,0x8004,0x8008,0x8010,0x8020,0x8040,0x8080,0x8100, + 0x8200,0x8400,0x8800,0x8c00,0x9000,0x9400,0x9800,0x9c00,0xa000,0xa400,0xa800,0xac00,0xb000,0xb400,0xb800,0xbc00, + 0xc000,0xc400,0xc800,0xcc00,0xd000,0xd400,0xd800,0xdc00,0xe000,0xe400,0xe800,0xec00,0xf000,0xf400,0xf800,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff}; + static AB1 shift[512]={ + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}; + union{AF1 f;AU1 u;}bits;bits.f=f;AU1 u=bits.u;AU1 i=u>>23;return (AU1)(base[i])+((u&0x7fffff)>>shift[i]);} +//------------------------------------------------------------------------------------------------------------------------------ + // Used to output packed constant. + A_STATIC AU1 AU1_AH2_AF2(inAF2 a){return AU1_AH1_AF1(a[0])+(AU1_AH1_AF1(a[1])<<16);} +#endif + +//============================================================================================================================== +// GLSL +//============================================================================================================================== +#if (defined(A_GLSL) && defined(A_GPU)) || (defined(ASPM_GLSL) && defined(ASPM_GPU)) + #ifndef A_SKIP_EXT + #ifdef A_HALF + #extension GL_EXT_shader_16bit_storage:require + #extension GL_EXT_shader_explicit_arithmetic_types:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_LONG + #extension GL_ARB_gpu_shader_int64:require + // TODO: Fixme to more portable extension!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #extension GL_NV_shader_atomic_int64:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_WAVE + #extension GL_KHR_shader_subgroup_arithmetic:require + #extension GL_KHR_shader_subgroup_ballot:require + #extension GL_KHR_shader_subgroup_quad:require + #extension GL_KHR_shader_subgroup_shuffle:require + #endif + #endif +//============================================================================================================================== + #define AP1 bool + #define AP2 bvec2 + #define AP3 bvec3 + #define AP4 bvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 vec2 + #define AF3 vec3 + #define AF4 vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uvec2 + #define AU3 uvec3 + #define AU4 uvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 ivec2 + #define ASU3 ivec3 + #define ASU4 ivec4 +//============================================================================================================================== + #define AF1_AU1(x) uintBitsToFloat(AU1(x)) + #define AF2_AU2(x) uintBitsToFloat(AU2(x)) + #define AF3_AU3(x) uintBitsToFloat(AU3(x)) + #define AF4_AU4(x) uintBitsToFloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) floatBitsToUint(AF1(x)) + #define AU2_AF2(x) floatBitsToUint(AF2(x)) + #define AU3_AF3(x) floatBitsToUint(AF3(x)) + #define AU4_AF4(x) floatBitsToUint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2_AF2 packHalf2x16 + #define AU1_AW2Unorm_AF2 packUnorm2x16 + #define AU1_AB4Unorm_AF4 packUnorm4x8 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF2_AH2_AU1 unpackHalf2x16 + #define AF2_AW2Unorm_AU1 unpackUnorm2x16 + #define AF4_AB4Unorm_AU1 unpackUnorm4x8 +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){return bitfieldExtract(src,ASU1(off),ASU1(bits));} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + // Proxy for V_BFI_B32 where the 'mask' is set as 'bits', 'mask=(1<>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #define AB1 uint8_t + #define AB2 u8vec2 + #define AB3 u8vec3 + #define AB4 u8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASB1 int8_t + #define ASB2 i8vec2 + #define ASB3 i8vec3 + #define ASB4 i8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + AB1 AB1_x(AB1 a){return AB1(a);} + AB2 AB2_x(AB1 a){return AB2(a,a);} + AB3 AB3_x(AB1 a){return AB3(a,a,a);} + AB4 AB4_x(AB1 a){return AB4(a,a,a,a);} + #define AB1_(a) AB1_x(AB1(a)) + #define AB2_(a) AB2_x(AB1(a)) + #define AB3_(a) AB3_x(AB1(a)) + #define AB4_(a) AB4_x(AB1(a)) + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 float16_t + #define AH2 f16vec2 + #define AH3 f16vec3 + #define AH4 f16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 u16vec2 + #define AW3 u16vec3 + #define AW4 u16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 i16vec2 + #define ASW3 i16vec3 + #define ASW4 i16vec4 +//============================================================================================================================== + #define AH2_AU1(x) unpackFloat2x16(AU1(x)) + AH4 AH4_AU2_x(AU2 x){return AH4(unpackFloat2x16(x.x),unpackFloat2x16(x.y));} + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) unpackUint2x16(AU1(x)) + #define AW4_AU2(x) unpackUint4x16(pack64(AU2(x))) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2(x) packFloat2x16(AH2(x)) + AU2 AU2_AH4_x(AH4 x){return AU2(packFloat2x16(x.xy),packFloat2x16(x.zw));} + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) packUint2x16(AW2(x)) + #define AU2_AW4(x) unpack32(packUint4x16(AW4(x))) +//============================================================================================================================== + #define AW1_AH1(x) halfBitsToUint16(AH1(x)) + #define AW2_AH2(x) halfBitsToUint16(AH2(x)) + #define AW3_AH3(x) halfBitsToUint16(AH3(x)) + #define AW4_AH4(x) halfBitsToUint16(AH4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AH1_AW1(x) uint16BitsToHalf(AW1(x)) + #define AH2_AW2(x) uint16BitsToHalf(AW2(x)) + #define AH3_AW3(x) uint16BitsToHalf(AW3(x)) + #define AH4_AW4(x) uint16BitsToHalf(AW4(x)) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFractH1(AH1 x){return fract(x);} + AH2 AFractH2(AH2 x){return fract(x);} + AH3 AFractH3(AH3 x){return fract(x);} + AH4 AFractH4(AH4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return mix(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return mix(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return mix(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of max3. + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of min3. + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return AH1_(1.0)/x;} + AH2 ARcpH2(AH2 x){return AH2_(1.0)/x;} + AH3 ARcpH3(AH3 x){return AH3_(1.0)/x;} + AH4 ARcpH4(AH4 x){return AH4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return AH1_(1.0)/sqrt(x);} + AH2 ARsqH2(AH2 x){return AH2_(1.0)/sqrt(x);} + AH3 ARsqH3(AH3 x){return AH3_(1.0)/sqrt(x);} + AH4 ARsqH4(AH4 x){return AH4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return clamp(x,AH1_(0.0),AH1_(1.0));} + AH2 ASatH2(AH2 x){return clamp(x,AH2_(0.0),AH2_(1.0));} + AH3 ASatH3(AH3 x){return clamp(x,AH3_(0.0),AH3_(1.0));} + AH4 ASatH4(AH4 x){return clamp(x,AH4_(0.0),AH4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 dvec2 + #define AD3 dvec3 + #define AD4 dvec4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 x){return fract(x);} + AD2 AFractD2(AD2 x){return fract(x);} + AD3 AFractD3(AD3 x){return fract(x);} + AD4 AFractD4(AD4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return mix(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return mix(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return mix(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return AD1_(1.0)/x;} + AD2 ARcpD2(AD2 x){return AD2_(1.0)/x;} + AD3 ARcpD3(AD3 x){return AD3_(1.0)/x;} + AD4 ARcpD4(AD4 x){return AD4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return AD1_(1.0)/sqrt(x);} + AD2 ARsqD2(AD2 x){return AD2_(1.0)/sqrt(x);} + AD3 ARsqD3(AD3 x){return AD3_(1.0)/sqrt(x);} + AD4 ARsqD4(AD4 x){return AD4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return clamp(x,AD1_(0.0),AD1_(1.0));} + AD2 ASatD2(AD2 x){return clamp(x,AD2_(0.0),AD2_(1.0));} + AD3 ASatD3(AD3 x){return clamp(x,AD3_(0.0),AD3_(1.0));} + AD4 ASatD4(AD4 x){return clamp(x,AD4_(0.0),AD4_(1.0));} + #endif +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #define AL1 uint64_t + #define AL2 u64vec2 + #define AL3 u64vec3 + #define AL4 u64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1 int64_t + #define ASL2 i64vec2 + #define ASL3 i64vec3 + #define ASL4 i64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AL1_AU2(x) packUint2x32(AU2(x)) + #define AU2_AL1(x) unpackUint2x32(AL1(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AL1_x(AL1 a){return AL1(a);} + AL2 AL2_x(AL1 a){return AL2(a,a);} + AL3 AL3_x(AL1 a){return AL3(a,a,a);} + AL4 AL4_x(AL1 a){return AL4(a,a,a,a);} + #define AL1_(a) AL1_x(AL1(a)) + #define AL2_(a) AL2_x(AL1(a)) + #define AL3_(a) AL3_x(AL1(a)) + #define AL4_(a) AL4_x(AL1(a)) +//============================================================================================================================== + AL1 AAbsSL1(AL1 a){return AL1(abs(ASL1(a)));} + AL2 AAbsSL2(AL2 a){return AL2(abs(ASL2(a)));} + AL3 AAbsSL3(AL3 a){return AL3(abs(ASL3(a)));} + AL4 AAbsSL4(AL4 a){return AL4(abs(ASL4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMaxSL1(AL1 a,AL1 b){return AL1(max(ASU1(a),ASU1(b)));} + AL2 AMaxSL2(AL2 a,AL2 b){return AL2(max(ASU2(a),ASU2(b)));} + AL3 AMaxSL3(AL3 a,AL3 b){return AL3(max(ASU3(a),ASU3(b)));} + AL4 AMaxSL4(AL4 a,AL4 b){return AL4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMinSL1(AL1 a,AL1 b){return AL1(min(ASU1(a),ASU1(b)));} + AL2 AMinSL2(AL2 a,AL2 b){return AL2(min(ASU2(a),ASU2(b)));} + AL3 AMinSL3(AL3 a,AL3 b){return AL3(min(ASU3(a),ASU3(b)));} + AL4 AMinSL4(AL4 a,AL4 b){return AL4(min(ASU4(a),ASU4(b)));} + #endif +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// WAVE OPERATIONS +//============================================================================================================================== + #ifdef A_WAVE + AF1 AWaveAdd(AF1 v){return subgroupAdd(v);} + AF2 AWaveAdd(AF2 v){return subgroupAdd(v);} + AF3 AWaveAdd(AF3 v){return subgroupAdd(v);} + AF4 AWaveAdd(AF4 v){return subgroupAdd(v);} + #endif +//============================================================================================================================== +#endif + +//============================================================================================================================== +// HLSL +//============================================================================================================================== +#if (defined(A_HLSL) && defined(A_GPU)) || (defined(ASPM_HLSL) && defined(ASPM_GPU)) + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 float2 + #define AF3 float3 + #define AF4 float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uint2 + #define AU3 uint3 + #define AU4 uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 int2 + #define ASU3 int3 + #define ASU4 int4 +//============================================================================================================================== + #define AF1_AU1(x) asfloat(AU1(x)) + #define AF2_AU2(x) asfloat(AU2(x)) + #define AF3_AU3(x) asfloat(AU3(x)) + #define AF4_AU4(x) asfloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) asuint(AF1(x)) + #define AU2_AF2(x) asuint(AF2(x)) + #define AU3_AF3(x) asuint(AF3(x)) + #define AU4_AF4(x) asuint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_AF2_x(AF2 a){return f32tof16(a.x)|(f32tof16(a.y)<<16);} + #define AU1_AH2_AF2(a) AU1_AH2_AF2_x(AF2(a)) + #define AU1_AB4Unorm_AF4(x) D3DCOLORtoUBYTE4(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AF2 AF2_AH2_AU1_x(AU1 x){return AF2(f16tof32(x&0xFFFF),f16tof32(x>>16));} + #define AF2_AH2_AU1(x) AF2_AH2_AU1_x(AU1(x)) +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){AU1 mask=(1<>off)&mask;} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){AU1 mask=(1<>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 min16float + #define AH2 min16float2 + #define AH3 min16float3 + #define AH4 min16float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 min16uint + #define AW2 min16uint2 + #define AW3 min16uint3 + #define AW4 min16uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 min16int + #define ASW2 min16int2 + #define ASW3 min16int3 + #define ASW4 min16int4 +//============================================================================================================================== + // Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). + // Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ + AH2 AH2_AU1_x(AU1 x){AF2 t=f16tof32(AU2(x&0xFFFF,x>>16));return AH2(t);} + AH4 AH4_AU2_x(AU2 x){return AH4(AH2_AU1_x(x.x),AH2_AU1_x(x.y));} + AW2 AW2_AU1_x(AU1 x){AU2 t=AU2(x&0xFFFF,x>>16);return AW2(t);} + AW4 AW4_AU2_x(AU2 x){return AW4(AW2_AU1_x(x.x),AW2_AU1_x(x.y));} + #define AH2_AU1(x) AH2_AU1_x(AU1(x)) + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) AW2_AU1_x(AU1(x)) + #define AW4_AU2(x) AW4_AU2_x(AU2(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_x(AH2 x){return f32tof16(x.x)+(f32tof16(x.y)<<16);} + AU2 AU2_AH4_x(AH4 x){return AU2(AU1_AH2_x(x.xy),AU1_AH2_x(x.zw));} + AU1 AU1_AW2_x(AW2 x){return AU1(x.x)+(AU1(x.y)<<16);} + AU2 AU2_AW4_x(AW4 x){return AU2(AU1_AW2_x(x.xy),AU1_AW2_x(x.zw));} + #define AU1_AH2(x) AU1_AH2_x(AH2(x)) + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) AU1_AW2_x(AW2(x)) + #define AU2_AW4(x) AU2_AW4_x(AW4(x)) +//============================================================================================================================== + // TODO: These are broken!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #define AW1_AH1(x) AW1(asuint(AF1(x))) + #define AW2_AH2(x) AW2(asuint(AF2(x))) + #define AW3_AH3(x) AW3(asuint(AF3(x))) + #define AW4_AH4(x) AW4(asuint(AF4(x))) +//------------------------------------------------------------------------------------------------------------------------------ + // TODO: These are broken!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #define AH1_AW1(x) AH1(asfloat(AU1(x))) + #define AH2_AW2(x) AH2(asfloat(AU2(x))) + #define AH3_AW3(x) AH3(asfloat(AU3(x))) + #define AH4_AW4(x) AH4(asfloat(AU4(x))) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F16 (note DX frac() is different). + AH1 AFractH1(AH1 x){return x-floor(x);} + AH2 AFractH2(AH2 x){return x-floor(x);} + AH3 AFractH3(AH3 x){return x-floor(x);} + AH4 AFractH4(AH4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return lerp(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return lerp(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return lerp(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return rcp(x);} + AH2 ARcpH2(AH2 x){return rcp(x);} + AH3 ARcpH3(AH3 x){return rcp(x);} + AH4 ARcpH4(AH4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return rsqrt(x);} + AH2 ARsqH2(AH2 x){return rsqrt(x);} + AH3 ARsqH3(AH3 x){return rsqrt(x);} + AH4 ARsqH4(AH4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return saturate(x);} + AH2 ASatH2(AH2 x){return saturate(x);} + AH3 ASatH3(AH3 x){return saturate(x);} + AH4 ASatH4(AH4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 double2 + #define AD3 double3 + #define AD4 double4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 a){return a-floor(a);} + AD2 AFractD2(AD2 a){return a-floor(a);} + AD3 AFractD3(AD3 a){return a-floor(a);} + AD4 AFractD4(AD4 a){return a-floor(a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return lerp(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return lerp(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return lerp(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return rcp(x);} + AD2 ARcpD2(AD2 x){return rcp(x);} + AD3 ARcpD3(AD3 x){return rcp(x);} + AD4 ARcpD4(AD4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return rsqrt(x);} + AD2 ARsqD2(AD2 x){return rsqrt(x);} + AD3 ARsqD3(AD3 x){return rsqrt(x);} + AD4 ARsqD4(AD4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return saturate(x);} + AD2 ASatD2(AD2 x){return saturate(x);} + AD3 ASatD3(AD3 x){return saturate(x);} + AD4 ASatD4(AD4 x){return saturate(x);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #endif +//============================================================================================================================== +#endif + +//============================================================================================================================== +// GPU COMMON +//============================================================================================================================== +#if defined(A_GPU) || (defined(ASPM_GPU) && !(defined(ASPM_OPENCL))) + // Negative and positive infinity. + #define A_INFN_F AF1_AU1(0x7f800000u) + #define A_INFP_F AF1_AU1(0xff800000u) +//------------------------------------------------------------------------------------------------------------------------------ + // Copy sign from 's' to positive 'd'. + AF1 ACpySgnF1(AF1 d,AF1 s){return AF1_AU1(AU1_AF1(d)|(AU1_AF1(s)&AU1_(0x80000000u)));} + AF2 ACpySgnF2(AF2 d,AF2 s){return AF2_AU2(AU2_AF2(d)|(AU2_AF2(s)&AU2_(0x80000000u)));} + AF3 ACpySgnF3(AF3 d,AF3 s){return AF3_AU3(AU3_AF3(d)|(AU3_AF3(s)&AU3_(0x80000000u)));} + AF4 ACpySgnF4(AF4 d,AF4 s){return AF4_AU4(AU4_AF4(d)|(AU4_AF4(s)&AU4_(0x80000000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Single operation to return (useful to create a mask to use in lerp for branch free logic), + // m=NaN := 0 + // m>=0 := 0 + // m<0 := 1 + // Uses the following useful floating point logic, + // saturate(+a*(-INF)==-INF) := 0 + // saturate( 0*(-INF)== NaN) := 0 + // saturate(-a*(-INF)==+INF) := 1 + AF1 ASignedF1(AF1 m){return ASatF1(m*AF1_(A_INFN_F));} + AF2 ASignedF2(AF2 m){return ASatF2(m*AF2_(A_INFN_F));} + AF3 ASignedF3(AF3 m){return ASatF3(m*AF3_(A_INFN_F));} + AF4 ASignedF4(AF4 m){return ASatF4(m*AF4_(A_INFN_F));} +//============================================================================================================================== + #ifdef A_HALF + #define A_INFN_H AH1_AW1(0x7c00u) + #define A_INFP_H AH1_AW1(0xfc00u) +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ACpySgnH1(AH1 d,AH1 s){return AH1_AW1(AW1_AH1(d)|(AW1_AH1(s)&AW1_(0x8000u)));} + AH2 ACpySgnH2(AH2 d,AH2 s){return AH2_AW2(AW2_AH2(d)|(AW2_AH2(s)&AW2_(0x8000u)));} + AH3 ACpySgnH3(AH3 d,AH3 s){return AH3_AW3(AW3_AH3(d)|(AW3_AH3(s)&AW3_(0x8000u)));} + AH4 ACpySgnH4(AH4 d,AH4 s){return AH4_AW4(AW4_AH4(d)|(AW4_AH4(s)&AW4_(0x8000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASignedH1(AH1 m){return ASatH1(m*AH1_(A_INFN_H));} + AH2 ASignedH2(AH2 m){return ASatH2(m*AH2_(A_INFN_H));} + AH3 ASignedH3(AH3 m){return ASatH3(m*AH3_(A_INFN_H));} + AH4 ASignedH4(AH4 m){return ASatH4(m*AH4_(A_INFN_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These support only positive inputs. +// Did not see value yet in specialization for range. +// Using quick testing, ended up mostly getting the same "best" approximation for various ranges. +// With hardware that can co-execute transcendentals, the value in approximations could be less than expected. +// However from a latency perspective, if execution of a transcendental is 4 clk, with no packed support, -> 8 clk total. +// And co-execution would require a compiler interleaving a lot of independent work for packed usage. +//------------------------------------------------------------------------------------------------------------------------------ +// The one Newton Raphson iteration form of rsq() was skipped (requires 6 ops total). +// Same with sqrt(), as this could be x*rsq() (7 ops). +//------------------------------------------------------------------------------------------------------------------------------ +// IDEAS +// ===== +// - Polaris hardware has 16-bit support, but non-double rate. +// Could be possible still get part double rate for some of this logic, +// by clearing out the lower half's sign when necessary and using 32-bit ops... +//============================================================================================================================== + #ifdef A_HALF + // Minimize squared error across full positive range, 2 ops. + // The 0x1de2 based approximation maps {0 to 1} input maps to < 1 output. + AH1 APrxLoSqrtH1(AH1 a){return AH1_AW1((AW1_AH1(a)>>AW1_(1))+AW1_(0x1de2));} + AH2 APrxLoSqrtH2(AH2 a){return AH2_AW2((AW2_AH2(a)>>AW2_(1))+AW2_(0x1de2));} +//------------------------------------------------------------------------------------------------------------------------------ + // Lower precision estimation, 1 op. + // Minimize squared error across {smallest normal to 16384.0}. + AH1 APrxLoRcpH1(AH1 a){return AH1_AW1(AW1_(0x7784)-AW1_AH1(a));} + AH2 APrxLoRcpH2(AH2 a){return AH2_AW2(AW2_(0x7784)-AW2_AH2(a));} +//------------------------------------------------------------------------------------------------------------------------------ + // Medium precision estimation, one Newton Raphson iteration, 3 ops. + AH1 APrxMedRcpH1(AH1 a){AH1 b=AH1_AW1(AW1_(0x778d)-AW1_AH1(a));return b*(-b*a+AH1_(2.0));} + AH2 APrxMedRcpH2(AH2 a){AH2 b=AH2_AW2(AW2_(0x778d)-AW2_AH2(a));return b*(-b*a+AH2_(2.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // Minimize squared error across {smallest normal to 16384.0}, 2 ops. + AH1 APrxLoRsqH1(AH1 a){return AH1_AW1(AW1_(0x59a3)-(AW1_AH1(a)>>AW1_(1)));} + AH2 APrxLoRsqH2(AH2 a){return AH2_AW2(AW2_(0x59a3)-(AW2_AH2(a)>>AW2_(1)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// FLOAT APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// Michal Drobot has an excellent presentation on these: "Low Level Optimizations For GCN", +// - Idea dates back to SGI, then to Quake 3, etc. +// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +// - sqrt(x)=rsqrt(x)*x +// - rcp(x)=rsqrt(x)*rsqrt(x) for positive x +// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +//------------------------------------------------------------------------------------------------------------------------------ +// These below are from perhaps less complete searching for optimal. +// Used FP16 normal range for testing with +4096 32-bit step size for sampling error. +// So these match up well with the half approximations. +//============================================================================================================================== + AF1 APrxLoSqrtF1(AF1 a){return AF1_AU1((AU1_AF1(a)>>AU1_(1))+AU1_(0x1fbc4639));} + AF1 APrxLoRcpF1(AF1 a){return AF1_AU1(AU1_(0x7ef07ebb)-AU1_AF1(a));} + AF1 APrxMedRcpF1(AF1 a){AF1 b=AF1_AU1(AU1_(0x7ef19fff)-AU1_AF1(a));return b*(-b*a+AF1_(2.0));} + AF1 APrxLoRsqF1(AF1 a){return AF1_AU1(AU1_(0x5f347d74)-(AU1_AF1(a)>>AU1_(1)));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PARABOLIC SIN & COS +//------------------------------------------------------------------------------------------------------------------------------ +// Approximate answers to transcendental questions. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Verify packed math ABS is correctly doing an AND. +//============================================================================================================================== + // Valid input range is {-1 to 1} representing {0 to 2 pi}. + // Output range is {-1/4 to -1/4} representing {-1 to 1}. + AF1 APSinF1(AF1 x){return x*abs(x)-x;} // MAD. + AF1 APCosF1(AF1 x){x=AFractF1(x*AF1_(0.5)+AF1_(0.75));x=x*AF1_(2.0)-AF1_(1.0);return APSinF1(x);} // 3x MAD, FRACT +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + // For a packed {sin,cos} pair, + // - Native takes 16 clocks and 4 issue slots (no packed transcendentals). + // - Parabolic takes 8 clocks and 8 issue slots (only fract is non-packed). + AH2 APSinH2(AH2 x){return x*abs(x)-x;} // AND,FMA + AH2 APCosH2(AH2 x){x=AFractH2(x*AH2_(0.5)+AH2_(0.75));x=x*AH2_(2.0)-AH2_(1.0);return APSinH2(x);} // 3x FMA, 2xFRACT, AND + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COLOR CONVERSIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These are all linear to/from some other space (where 'linear' has been shortened out of the function name). +// So 'ToGamma' is 'LinearToGamma', and 'FromGamma' is 'LinearFromGamma'. +// These are branch free implementations. +// The AToSrgbF1() function is useful for stores for compute shaders for GPUs without hardware linear->sRGB store conversion. +//------------------------------------------------------------------------------------------------------------------------------ +// TRANSFER FUNCTIONS +// ================== +// 709 ..... Rec709 used for some HDTVs +// Gamma ... Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native +// Pq ...... PQ native for HDR10 +// Srgb .... The sRGB output, typical of PC displays, useful for 10-bit output, or storing to 8-bit UNORM without SRGB type +// Two ..... Gamma 2.0, fastest conversion (useful for intermediate pass approximations) +//------------------------------------------------------------------------------------------------------------------------------ +// FOR PQ +// ====== +// Both input and output is {0.0-1.0}, and where output 1.0 represents 10000.0 cd/m^2. +// All constants are only specified to FP32 precision. +// External PQ source reference, +// - https://github.com/ampas/aces-dev/blob/master/transforms/ctl/utilities/ACESlib.Utilities_Color.a1.0.1.ctl +//------------------------------------------------------------------------------------------------------------------------------ +// PACKED VERSIONS +// =============== +// These are the A*H2() functions. +// There is no PQ functions as FP16 seemed to not have enough precision for the conversion. +// The remaining functions are "good enough" for 8-bit, and maybe 10-bit if not concerned about a few 1-bit errors. +// Precision is lowest in the 709 conversion, higher in sRGB, higher still in Two and Gamma (when using 2.2 at least). +//------------------------------------------------------------------------------------------------------------------------------ +// NOTES +// ===== +// Could be faster for PQ conversions to be in ALU or a texture lookup depending on usage case. +//============================================================================================================================== + AF1 ATo709F1(AF1 c){return max(min(c*AF1_(4.5),AF1_(0.018)),AF1_(1.099)*pow(c,AF1_(0.45))-AF1_(0.099));} +//------------------------------------------------------------------------------------------------------------------------------ + // Note 'rcpX' is '1/x', where the 'x' is what would be used in AFromGamma(). + AF1 AToGammaF1(AF1 c,AF1 rcpX){return pow(c,rcpX);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToPqF1(AF1 x){AF1 p=pow(x,AF1_(0.159302)); + return pow((AF1_(0.835938)+AF1_(18.8516)*p)/(AF1_(1.0)+AF1_(18.6875)*p),AF1_(78.8438));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToSrgbF1(AF1 c){return max(min(c*AF1_(12.92),AF1_(0.0031308)),AF1_(1.055)*pow(c,AF1_(0.41666))-AF1_(0.055));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToTwoF1(AF1 c){return sqrt(c);} +//============================================================================================================================== + AF1 AFrom709F1(AF1 c){return max(min(c*AF1_(1.0/4.5),AF1_(0.081)), + pow((c+AF1_(0.099))*(AF1_(1.0)/(AF1_(1.099))),AF1_(1.0/0.45)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromGammaF1(AF1 c,AF1 x){return pow(c,x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromPqF1(AF1 x){AF1 p=pow(x,AF1_(0.0126833)); + return pow(ASatF1(p-AF1_(0.835938))/(AF1_(18.8516)-AF1_(18.6875)*p),AF1_(6.27739));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromSrgbF1(AF1 c){return max(min(c*AF1_(1.0/12.92),AF1_(0.04045)), + pow((c+AF1_(0.055))*(AF1_(1.0)/AF1_(1.055)),AF1_(2.4)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromTwoF1(AF1 c){return c*c;} +//============================================================================================================================== + #ifdef A_HALF + AH2 ATo709H2(AH2 c){return max(min(c*AH2_(4.5),AH2_(0.018)),AH2_(1.099)*pow(c,AH2_(0.45))-AH2_(0.099));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AToGammaH2(AH2 c,AH1 rcpX){return pow(c,AH2_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AToSrgbH2(AH2 c){return max(min(c*AH2_(12.92),AH2_(0.0031308)),AH2_(1.055)*pow(c,AH2_(0.41666))-AH2_(0.055));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AToTwoH2(AH2 c){return sqrt(c);} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH2 AFrom709H2(AH2 c){return max(min(c*AH2_(1.0/4.5),AH2_(0.081)), + pow((c+AH2_(0.099))*(AH2_(1.0)/(AH2_(1.099))),AH2_(1.0/0.45)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AFromGammaH2(AH2 c,AH1 x){return pow(c,AH2_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AFromSrgbH2(AH2 c){return max(min(c*AH2_(1.0/12.92),AH2_(0.04045)), + pow((c+AH2_(0.055))*(AH2_(1.0)/AH2_(1.055)),AH2_(2.4)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AFromTwoH2(AH2 c){return c*c;} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CS REMAP +//============================================================================================================================== + // Simple remap 64x1 to 8x8 with rotated 2x2 pixel quads in quad linear. + // 543210 + // ====== + // ..xxx. + // yy...y + AU2 ARmp8x8(AU1 a){return AU2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} +//============================================================================================================================== + // More complex remap 64x1 to 8x8 which is necessary for 2D wave reductions. + // 543210 + // ====== + // .xx..x + // y..yy. + // Details, + // LANE TO 8x8 MAPPING + // =================== + // 00 01 08 09 10 11 18 19 + // 02 03 0a 0b 12 13 1a 1b + // 04 05 0c 0d 14 15 1c 1d + // 06 07 0e 0f 16 17 1e 1f + // 20 21 28 29 30 31 38 39 + // 22 23 2a 2b 32 33 3a 3b + // 24 25 2c 2d 34 35 3c 3d + // 26 27 2e 2f 36 37 3e 3f + AU2 ARmpRed8x8(AU1 a){return AU2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} +#endif + +//============================================================================================================================== +// GPU/CPU PORTABILITY +//------------------------------------------------------------------------------------------------------------------------------ +// This is the GPU implementation. +// See the CPU implementation for docs. +//============================================================================================================================== +#if defined(A_GPU) || (defined(ASPM_GPU) && !(defined(ASPM_OPENCL))) + #define A_TRUE true + #define A_FALSE false + #define A_STATIC +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD2 + #define retAD3 AD3 + #define retAD4 AD4 + #define retAF2 AF2 + #define retAF3 AF3 + #define retAF4 AF4 + #define retAL2 AL2 + #define retAL3 AL3 + #define retAL4 AL4 + #define retAU2 AU2 + #define retAU3 AU3 + #define retAU4 AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 in AD2 + #define inAD3 in AD3 + #define inAD4 in AD4 + #define inAF2 in AF2 + #define inAF3 in AF3 + #define inAF4 in AF4 + #define inAL2 in AL2 + #define inAL3 in AL3 + #define inAL4 in AL4 + #define inAU2 in AU2 + #define inAU3 in AU3 + #define inAU4 in AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 inout AD2 + #define inoutAD3 inout AD3 + #define inoutAD4 inout AD4 + #define inoutAF2 inout AF2 + #define inoutAF3 inout AF3 + #define inoutAF4 inout AF4 + #define inoutAL2 inout AL2 + #define inoutAL3 inout AL3 + #define inoutAL4 inout AL4 + #define inoutAU2 inout AU2 + #define inoutAU3 inout AU3 + #define inoutAU4 inout AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 out AD2 + #define outAD3 out AD3 + #define outAD4 out AD4 + #define outAF2 out AF2 + #define outAF3 out AF3 + #define outAF4 out AF4 + #define outAL2 out AL2 + #define outAL3 out AL3 + #define outAL4 out AL4 + #define outAU2 out AU2 + #define outAU3 out AU3 + #define outAU4 out AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD2 x + #define varAD3(x) AD3 x + #define varAD4(x) AD4 x + #define varAF2(x) AF2 x + #define varAF3(x) AF3 x + #define varAF4(x) AF4 x + #define varAL2(x) AL2 x + #define varAL3(x) AL3 x + #define varAL4(x) AL4 x + #define varAU2(x) AU2 x + #define varAU3(x) AU3 x + #define varAU4(x) AU4 x +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) AD2(x,y) + #define initAD3(x,y,z) AD3(x,y,z) + #define initAD4(x,y,z,w) AD4(x,y,z,w) + #define initAF2(x,y) AF2(x,y) + #define initAF3(x,y,z) AF3(x,y,z) + #define initAF4(x,y,z,w) AF4(x,y,z,w) + #define initAL2(x,y) AL2(x,y) + #define initAL3(x,y,z) AL3(x,y,z) + #define initAL4(x,y,z,w) AL4(x,y,z,w) + #define initAU2(x,y) AU2(x,y) + #define initAU3(x,y,z) AU3(x,y,z) + #define initAU4(x,y,z,w) AU4(x,y,z,w) +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//============================================================================================================================== + #define AAbsD1(a) abs(AD1(a)) + #define AAbsF1(a) abs(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ACosD1(a) cos(AD1(a)) + #define ACosF1(a) cos(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ADotD2(a,b) dot(AD2(a),AD2(b)) + #define ADotD3(a,b) dot(AD3(a),AD3(b)) + #define ADotD4(a,b) dot(AD4(a),AD4(b)) + #define ADotF2(a,b) dot(AF2(a),AF2(b)) + #define ADotF3(a,b) dot(AF3(a),AF3(b)) + #define ADotF4(a,b) dot(AF4(a),AF4(b)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AExp2D1(a) exp2(AD1(a)) + #define AExp2F1(a) exp2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AFloorD1(a) floor(AD1(a)) + #define AFloorF1(a) floor(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ALog2D1(a) log2(AD1(a)) + #define ALog2F1(a) log2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMaxD1(a,b) min(a,b) + #define AMaxF1(a,b) min(a,b) + #define AMaxL1(a,b) min(a,b) + #define AMaxU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMinD1(a,b) min(a,b) + #define AMinF1(a,b) min(a,b) + #define AMinL1(a,b) min(a,b) + #define AMinU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASinD1(a) sin(AD1(a)) + #define ASinF1(a) sin(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASqrtD1(a) sqrt(AD1(a)) + #define ASqrtF1(a) sqrt(AF1(a)) +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + #define APowD1(a,b) pow(AD1(a),AF1(b)) + #define APowF1(a,b) pow(AF1(a),AF1(b)) +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + #ifdef A_DUBL + AD2 opAAbsD2(outAD2 d,inAD2 a){d=abs(a);return d;} + AD3 opAAbsD3(outAD3 d,inAD3 a){d=abs(a);return d;} + AD4 opAAbsD4(outAD4 d,inAD4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d=a+b;return d;} + AD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d=a+b;return d;} + AD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opACpyD2(outAD2 d,inAD2 a){d=a;return d;} + AD3 opACpyD3(outAD3 d,inAD3 a){d=a;return d;} + AD4 opACpyD4(outAD4 d,inAD4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d=ALerpD2(a,b,c);return d;} + AD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d=ALerpD3(a,b,c);return d;} + AD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d=ALerpD4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d=ALerpD2(a,b,AD2_(c));return d;} + AD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d=ALerpD3(a,b,AD3_(c));return d;} + AD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d=ALerpD4(a,b,AD4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d=max(a,b);return d;} + AD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d=max(a,b);return d;} + AD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d=min(a,b);return d;} + AD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d=min(a,b);return d;} + AD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d=a*b;return d;} + AD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d=a*b;return d;} + AD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d=a*AD2_(b);return d;} + AD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d=a*AD3_(b);return d;} + AD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d=a*AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opANegD2(outAD2 d,inAD2 a){d=-a;return d;} + AD3 opANegD3(outAD3 d,inAD3 a){d=-a;return d;} + AD4 opANegD4(outAD4 d,inAD4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opARcpD2(outAD2 d,inAD2 a){d=ARcpD2(a);return d;} + AD3 opARcpD3(outAD3 d,inAD3 a){d=ARcpD3(a);return d;} + AD4 opARcpD4(outAD4 d,inAD4 a){d=ARcpD4(a);return d;} + #endif +//============================================================================================================================== + AF2 opAAbsF2(outAF2 d,inAF2 a){d=abs(a);return d;} + AF3 opAAbsF3(outAF3 d,inAF3 a){d=abs(a);return d;} + AF4 opAAbsF4(outAF4 d,inAF4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d=a+b;return d;} + AF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d=a+b;return d;} + AF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opACpyF2(outAF2 d,inAF2 a){d=a;return d;} + AF3 opACpyF3(outAF3 d,inAF3 a){d=a;return d;} + AF4 opACpyF4(outAF4 d,inAF4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d=ALerpF2(a,b,c);return d;} + AF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d=ALerpF3(a,b,c);return d;} + AF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d=ALerpF4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d=ALerpF2(a,b,AF2_(c));return d;} + AF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d=ALerpF3(a,b,AF3_(c));return d;} + AF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d=ALerpF4(a,b,AF4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d=max(a,b);return d;} + AF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d=max(a,b);return d;} + AF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d=min(a,b);return d;} + AF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d=min(a,b);return d;} + AF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d=a*b;return d;} + AF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d=a*b;return d;} + AF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d=a*AF2_(b);return d;} + AF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d=a*AF3_(b);return d;} + AF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d=a*AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opANegF2(outAF2 d,inAF2 a){d=-a;return d;} + AF3 opANegF3(outAF3 d,inAF3 a){d=-a;return d;} + AF4 opANegF4(outAF4 d,inAF4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opARcpF2(outAF2 d,inAF2 a){d=ARcpF2(a);return d;} + AF3 opARcpF3(outAF3 d,inAF3 a){d=ARcpF3(a);return d;} + AF4 opARcpF4(outAF4 d,inAF4 a){d=ARcpF4(a);return d;} +#endif +#endif // USE_CMP_FIDELITY_FX_H + +#endif // Common_Def.h diff --git a/WickedEngine/shaders/emittedparticlePS_soft.hlsl b/WickedEngine/shaders/emittedparticlePS_soft.hlsl index cb4ac053b..27d04205e 100644 --- a/WickedEngine/shaders/emittedparticlePS_soft.hlsl +++ b/WickedEngine/shaders/emittedparticlePS_soft.hlsl @@ -4,6 +4,12 @@ #include "ShaderInterop_EmittedParticle.h" #include "objectHF.hlsli" +#ifdef EMITTEDPARTICLE_DISTORTION +static const uint SLOT = NORMALMAP; +#else +static const uint SLOT = BASECOLORMAP; +#endif // EMITTEDPARTICLE_DISTORTION + [earlydepthstencil] float4 main(VertextoPixel input) : SV_TARGET { @@ -12,14 +18,14 @@ float4 main(VertextoPixel input) : SV_TARGET float4 color = 1; [branch] - if (material.textures[BASECOLORMAP].IsValid()) + if (material.textures[SLOT].IsValid()) { - color = material.textures[BASECOLORMAP].Sample(sampler_linear_clamp, input.tex.xyxy); + color = material.textures[SLOT].Sample(sampler_linear_clamp, input.tex.xyxy); [branch] if (xEmitterOptions & EMITTER_OPTION_BIT_FRAME_BLENDING_ENABLED) { - float4 color2 = material.textures[BASECOLORMAP].Sample(sampler_linear_clamp, input.tex.zwzw); + float4 color2 = material.textures[SLOT].Sample(sampler_linear_clamp, input.tex.zwzw); color = lerp(color, color2, input.frameBlend); } } @@ -43,7 +49,6 @@ float4 main(VertextoPixel input) : SV_TARGET #ifdef EMITTEDPARTICLE_DISTORTION // just make normal maps blendable: - color.rgb = ApplySRGBCurve_Fast(color.rgb); // note: This texture uses basecolormap slot, and this slot is using SRGB descriptor, so we correct it here for normal map color.rgb = color.rgb - 0.5f; #endif // EMITTEDPARTICLE_DISTORTION diff --git a/WickedEngine/shaders/lightCullingCS.hlsl b/WickedEngine/shaders/lightCullingCS.hlsl index f0ef825f3..ca5184705 100644 --- a/WickedEngine/shaders/lightCullingCS.hlsl +++ b/WickedEngine/shaders/lightCullingCS.hlsl @@ -236,6 +236,8 @@ void main(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : { if (entity.GetFlags() & ENTITY_FLAG_LIGHT_STATIC) break; // static lights will be skipped here (they are used at lightmap baking) + if (!any(entity.GetColor().rgb)) + break; float3 positionVS = mul(GetCamera().view, float4(entity.position, 1)).xyz; Sphere sphere = { positionVS.xyz, entity.GetRange() + entity.GetLength() }; if (SphereInsideFrustum(sphere, GroupFrustum, nearClipVS, maxDepthVS)) @@ -258,6 +260,8 @@ void main(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : { if (entity.GetFlags() & ENTITY_FLAG_LIGHT_STATIC) break; // static lights will be skipped here (they are used at lightmap baking) + if (!any(entity.GetColor().rgb)) + break; float3 positionVS = mul(GetCamera().view, float4(entity.position, 1)).xyz; float3 directionVS = mul((float3x3)GetCamera().view, entity.GetDirection()); // Construct a tight fitting sphere around the spotlight cone: @@ -284,6 +288,8 @@ void main(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : { if (entity.GetFlags() & ENTITY_FLAG_LIGHT_STATIC) break; // static lights will be skipped here (they are used at lightmap baking) + if (!any(entity.GetColor().rgb)) + break; AppendEntity_Transparent(i); AppendEntity_Opaque(i); } diff --git a/WickedEngine/shaders/objectHF.hlsli b/WickedEngine/shaders/objectHF.hlsli index f1d4c2659..b4e5ead0c 100644 --- a/WickedEngine/shaders/objectHF.hlsli +++ b/WickedEngine/shaders/objectHF.hlsli @@ -17,6 +17,7 @@ #endif #ifdef WATER +#define DISABLE_ENVMAPS #define DISABLE_VOXELGI #endif diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index c6289f972..cc6d91f24 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -5,7 +5,7 @@ namespace wi { // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! - static constexpr uint64_t __archiveVersion = 88; + static constexpr uint64_t __archiveVersion = 89; // this is the version number of which below the archive is not compatible with the current version static constexpr uint64_t __archiveVersionBarrier = 22; diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h index ebf9acfa4..b1962fbde 100644 --- a/WickedEngine/wiEnums.h +++ b/WickedEngine/wiEnums.h @@ -246,6 +246,9 @@ namespace wi::enums CSTYPE_GENERATEMIPCHAINCUBE_FLOAT4, CSTYPE_GENERATEMIPCHAINCUBEARRAY_UNORM4, CSTYPE_GENERATEMIPCHAINCUBEARRAY_FLOAT4, + CSTYPE_BLOCKCOMPRESS_BC1, + CSTYPE_BLOCKCOMPRESS_BC3, + CSTYPE_BLOCKCOMPRESS_BC5, CSTYPE_FILTERENVMAP, CSTYPE_COPYTEXTURE2D_UNORM4, CSTYPE_COPYTEXTURE2D_FLOAT4, diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp index fcd9b14c0..73e4ab925 100644 --- a/WickedEngine/wiGUI.cpp +++ b/WickedEngine/wiGUI.cpp @@ -428,6 +428,10 @@ namespace wi::gui { return font.GetTextA(); } + std::string Widget::GetTooltip() const + { + return tooltipFont.GetTextA(); + } void Widget::SetText(const char* value) { font.SetText(value); diff --git a/WickedEngine/wiGUI.h b/WickedEngine/wiGUI.h index 7b395e2e8..e8f522467 100644 --- a/WickedEngine/wiGUI.h +++ b/WickedEngine/wiGUI.h @@ -272,6 +272,7 @@ namespace wi::gui const std::string& GetName() const; void SetName(const std::string& value); std::string GetText() const; + std::string GetTooltip() const; void SetText(const char* value); void SetText(const std::string& value); void SetText(std::string&& value); diff --git a/WickedEngine/wiGraphics.h b/WickedEngine/wiGraphics.h index cbe7fda7f..cdd80772c 100644 --- a/WickedEngine/wiGraphics.h +++ b/WickedEngine/wiGraphics.h @@ -1494,6 +1494,146 @@ namespace wi::graphics return Format::UNKNOWN; } } + constexpr const char* GetFormatString(Format format) + { + switch (format) + { + case wi::graphics::Format::UNKNOWN: + return "UNKNOWN"; + case wi::graphics::Format::R32G32B32A32_FLOAT: + return "R32G32B32A32_FLOAT"; + case wi::graphics::Format::R32G32B32A32_UINT: + return "R32G32B32A32_UINT"; + case wi::graphics::Format::R32G32B32A32_SINT: + return "R32G32B32A32_SINT"; + case wi::graphics::Format::R32G32B32_FLOAT: + return "R32G32B32_FLOAT"; + case wi::graphics::Format::R32G32B32_UINT: + return "R32G32B32_UINT"; + case wi::graphics::Format::R32G32B32_SINT: + return "R32G32B32_SINT"; + case wi::graphics::Format::R16G16B16A16_FLOAT: + return "R16G16B16A16_FLOAT"; + case wi::graphics::Format::R16G16B16A16_UNORM: + return "R16G16B16A16_UNORM"; + case wi::graphics::Format::R16G16B16A16_UINT: + return "R16G16B16A16_UINT"; + case wi::graphics::Format::R16G16B16A16_SNORM: + return "R16G16B16A16_SNORM"; + case wi::graphics::Format::R16G16B16A16_SINT: + return "R16G16B16A16_SINT"; + case wi::graphics::Format::R32G32_FLOAT: + return "R32G32_FLOAT"; + case wi::graphics::Format::R32G32_UINT: + return "R32G32_UINT"; + case wi::graphics::Format::R32G32_SINT: + return "R32G32_SINT"; + case wi::graphics::Format::D32_FLOAT_S8X24_UINT: + return "D32_FLOAT_S8X24_UINT"; + case wi::graphics::Format::R10G10B10A2_UNORM: + return "R10G10B10A2_UNORM"; + case wi::graphics::Format::R10G10B10A2_UINT: + return "R10G10B10A2_UINT"; + case wi::graphics::Format::R11G11B10_FLOAT: + return "R11G11B10_FLOAT"; + case wi::graphics::Format::R8G8B8A8_UNORM: + return "R8G8B8A8_UNORM"; + case wi::graphics::Format::R8G8B8A8_UNORM_SRGB: + return "R8G8B8A8_UNORM_SRGB"; + case wi::graphics::Format::R8G8B8A8_UINT: + return "R8G8B8A8_UINT"; + case wi::graphics::Format::R8G8B8A8_SNORM: + return "R8G8B8A8_SNORM"; + case wi::graphics::Format::R8G8B8A8_SINT: + return "R8G8B8A8_SINT"; + case wi::graphics::Format::B8G8R8A8_UNORM: + return "B8G8R8A8_UNORM"; + case wi::graphics::Format::B8G8R8A8_UNORM_SRGB: + return "B8G8R8A8_UNORM_SRGB"; + case wi::graphics::Format::R16G16_FLOAT: + return "R16G16_FLOAT"; + case wi::graphics::Format::R16G16_UNORM: + return "R16G16_UNORM"; + case wi::graphics::Format::R16G16_UINT: + return "R16G16_UINT"; + case wi::graphics::Format::R16G16_SNORM: + return "R16G16_SNORM"; + case wi::graphics::Format::R16G16_SINT: + return "R16G16_SINT"; + case wi::graphics::Format::D32_FLOAT: + return "D32_FLOAT"; + case wi::graphics::Format::R32_FLOAT: + return "R32_FLOAT"; + case wi::graphics::Format::R32_UINT: + return "R32_UINT"; + case wi::graphics::Format::R32_SINT: + return "R32_SINT"; + case wi::graphics::Format::D24_UNORM_S8_UINT: + return "D24_UNORM_S8_UINT"; + case wi::graphics::Format::R9G9B9E5_SHAREDEXP: + return "R9G9B9E5_SHAREDEXP"; + case wi::graphics::Format::R8G8_UNORM: + return "R8G8_UNORM"; + case wi::graphics::Format::R8G8_UINT: + return "R8G8_UINT"; + case wi::graphics::Format::R8G8_SNORM: + return "R8G8_SNORM"; + case wi::graphics::Format::R8G8_SINT: + return "R8G8_SINT"; + case wi::graphics::Format::R16_FLOAT: + return "R16_FLOAT"; + case wi::graphics::Format::D16_UNORM: + return "D16_UNORM"; + case wi::graphics::Format::R16_UNORM: + return "R16_UNORM"; + case wi::graphics::Format::R16_UINT: + return "R16_UINT"; + case wi::graphics::Format::R16_SNORM: + return "R16_SNORM"; + case wi::graphics::Format::R16_SINT: + return "R16_SINT"; + case wi::graphics::Format::R8_UNORM: + return "R8_UNORM"; + case wi::graphics::Format::R8_UINT: + return "R8_UINT"; + case wi::graphics::Format::R8_SNORM: + return "R8_SNORM"; + case wi::graphics::Format::R8_SINT: + return "R8_SINT"; + case wi::graphics::Format::BC1_UNORM: + return "BC1_UNORM"; + case wi::graphics::Format::BC1_UNORM_SRGB: + return "BC1_UNORM_SRGB"; + case wi::graphics::Format::BC2_UNORM: + return "BC2_UNORM"; + case wi::graphics::Format::BC2_UNORM_SRGB: + return "BC2_UNORM_SRGB"; + case wi::graphics::Format::BC3_UNORM: + return "BC3_UNORM"; + case wi::graphics::Format::BC3_UNORM_SRGB: + return "BC3_UNORM_SRGB"; + case wi::graphics::Format::BC4_UNORM: + return "BC4_UNORM"; + case wi::graphics::Format::BC4_SNORM: + return "BC4_SNORM"; + case wi::graphics::Format::BC5_UNORM: + return "BC5_UNORM"; + case wi::graphics::Format::BC5_SNORM: + return "BC5_SNORM"; + case wi::graphics::Format::BC6H_UF16: + return "BC6H_UF16"; + case wi::graphics::Format::BC6H_SF16: + return "BC6H_SF16"; + case wi::graphics::Format::BC7_UNORM: + return "BC7_UNORM"; + case wi::graphics::Format::BC7_UNORM_SRGB: + return "BC7_UNORM_SRGB"; + case wi::graphics::Format::NV12: + return "NV12"; + default: + return ""; + } + } constexpr IndexBufferFormat GetIndexBufferFormat(Format format) { switch (format) @@ -1514,6 +1654,38 @@ namespace wi::graphics { return ((value + alignment - 1) / alignment) * alignment; } + constexpr uint32_t GetMipCount(uint32_t width, uint32_t height, uint32_t depth = 1u) + { + uint32_t mips = 1; + while (width > 1u || height > 1u || depth > 1u) + { + width = std::max(1u, width >> 1u); + height = std::max(1u, height >> 1u); + depth = std::max(1u, depth >> 1u); + mips++; + } + return mips; + } + constexpr size_t ComputeTextureMemorySizeInBytes(const TextureDesc& desc) + { + size_t size = 0; + const uint32_t bytes_per_block = GetFormatStride(desc.format); + const uint32_t pixels_per_block = GetFormatBlockSize(desc.format); + const uint32_t num_blocks_x = desc.width / pixels_per_block; + const uint32_t num_blocks_y = desc.height / pixels_per_block; + const uint32_t mips = desc.mip_levels == 0 ? GetMipCount(desc.width, desc.height, desc.depth) : desc.mip_levels; + for (uint32_t layer = 0; layer < desc.array_size; ++layer) + { + for (uint32_t mip = 0; mip < mips; ++mip) + { + const uint32_t width = std::max(1u, num_blocks_x >> mip); + const uint32_t height = std::max(1u, num_blocks_y >> mip); + const uint32_t depth = std::max(1u, desc.depth >> mip); + size += width * height * depth * bytes_per_block; + } + } + return size; + } // Deprecated, kept for back-compat: diff --git a/WickedEngine/wiGraphicsDevice_DX12.cpp b/WickedEngine/wiGraphicsDevice_DX12.cpp index 5256c1cd0..d90b8876b 100644 --- a/WickedEngine/wiGraphicsDevice_DX12.cpp +++ b/WickedEngine/wiGraphicsDevice_DX12.cpp @@ -3464,7 +3464,7 @@ using namespace dx12_internal; if (texture->desc.mip_levels == 0) { - texture->desc.mip_levels = (uint32_t)log2(std::max(texture->desc.width, texture->desc.height)) + 1; + texture->desc.mip_levels = GetMipCount(texture->desc.width, texture->desc.height, texture->desc.depth); } internal_state->total_size = 0; diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index bf4a29d7a..f245caeff 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -3946,7 +3946,7 @@ using namespace vulkan_internal; if (texture->desc.mip_levels == 0) { - texture->desc.mip_levels = (uint32_t)log2(std::max(texture->desc.width, texture->desc.height)) + 1; + texture->desc.mip_levels = GetMipCount(texture->desc.width, texture->desc.height, texture->desc.depth); } VkImageCreateInfo imageInfo = {}; diff --git a/WickedEngine/wiRenderPath2D.cpp b/WickedEngine/wiRenderPath2D.cpp index de7c8626f..86d0c7727 100644 --- a/WickedEngine/wiRenderPath2D.cpp +++ b/WickedEngine/wiRenderPath2D.cpp @@ -127,7 +127,7 @@ namespace wi wi::image::SetCanvas(*this); wi::font::SetCanvas(*this); - wi::renderer::ProcessDeferredMipGenRequests(cmd); + wi::renderer::ProcessDeferredTextureRequests(cmd); if (GetGUIBlurredBackground() != nullptr) { diff --git a/WickedEngine/wiRenderPath3D.cpp b/WickedEngine/wiRenderPath3D.cpp index 9672ee56a..164c6e176 100644 --- a/WickedEngine/wiRenderPath3D.cpp +++ b/WickedEngine/wiRenderPath3D.cpp @@ -708,7 +708,7 @@ namespace wi // Preparing the frame: CommandList cmd = device->BeginCommandList(); CommandList cmd_prepareframe = cmd; - wi::renderer::ProcessDeferredMipGenRequests(cmd); // Execute it first thing in the frame here, on main thread, to not allow other thread steal it and execute on different command list! + wi::renderer::ProcessDeferredTextureRequests(cmd); // Execute it first thing in the frame here, on main thread, to not allow other thread steal it and execute on different command list! wi::jobsystem::Execute(ctx, [this, cmd](wi::jobsystem::JobArgs args) { GraphicsDevice* device = wi::graphics::GetDevice(); wi::renderer::BindCameraCB( diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 65ab68971..f73c52f37 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -130,6 +130,7 @@ wi::vector paintrads; wi::SpinLock deferredMIPGenLock; wi::vector> deferredMIPGens; +wi::vector> deferredBCQueue; static const uint32_t vertexCount_uvsphere = arraysize(UVSPHERE); static const uint32_t vertexCount_cone = arraysize(CONE); @@ -891,6 +892,9 @@ void LoadShaders() wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_GENERATEMIPCHAINCUBE_FLOAT4], "generateMIPChainCubeCS_float4.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_GENERATEMIPCHAINCUBEARRAY_UNORM4], "generateMIPChainCubeArrayCS_unorm4.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_GENERATEMIPCHAINCUBEARRAY_FLOAT4], "generateMIPChainCubeArrayCS_float4.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_BLOCKCOMPRESS_BC1], "blockcompressCS_BC1.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_BLOCKCOMPRESS_BC3], "blockcompressCS_BC3.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_BLOCKCOMPRESS_BC5], "blockcompressCS_BC5.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_FILTERENVMAP], "filterEnvMapCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_COPYTEXTURE2D_UNORM4], "copytexture2D_unorm4CS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_COPYTEXTURE2D_FLOAT4], "copytexture2D_float4CS.cso"); }); @@ -2865,7 +2869,7 @@ void RenderImpostors( } } -void ProcessDeferredMipGenRequests(CommandList cmd) +void ProcessDeferredTextureRequests(CommandList cmd) { deferredMIPGenLock.lock(); for (auto& it : deferredMIPGens) @@ -2875,6 +2879,11 @@ void ProcessDeferredMipGenRequests(CommandList cmd) GenerateMipChain(it.first, MIPGENFILTER_LINEAR, cmd, mipopt); } deferredMIPGens.clear(); + for (auto& it : deferredBCQueue) + { + BlockCompress(it.first, it.second, cmd); + } + deferredBCQueue.clear(); deferredMIPGenLock.unlock(); } @@ -8367,7 +8376,6 @@ void GenerateMipChain(const Texture& texture, MIPGENFILTER filter, CommandList c { GPUBarrier barriers[] = { - GPUBarrier::Memory(), GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, options.arrayIndex * 6 + 0), GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, options.arrayIndex * 6 + 1), GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, options.arrayIndex * 6 + 2), @@ -8401,6 +8409,18 @@ void GenerateMipChain(const Texture& texture, MIPGENFILTER filter, CommandList c for (uint32_t i = 0; i < desc.mip_levels - 1; ++i) { + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&texture, texture.desc.layout, ResourceState::UNORDERED_ACCESS, i + 1, 0), + GPUBarrier::Image(&texture, texture.desc.layout, ResourceState::UNORDERED_ACCESS, i + 1, 1), + GPUBarrier::Image(&texture, texture.desc.layout, ResourceState::UNORDERED_ACCESS, i + 1, 2), + GPUBarrier::Image(&texture, texture.desc.layout, ResourceState::UNORDERED_ACCESS, i + 1, 3), + GPUBarrier::Image(&texture, texture.desc.layout, ResourceState::UNORDERED_ACCESS, i + 1, 4), + GPUBarrier::Image(&texture, texture.desc.layout, ResourceState::UNORDERED_ACCESS, i + 1, 5), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + mipgen.texture_output = device->GetDescriptorIndex(&texture, SubresourceType::UAV, i + 1); mipgen.texture_input = device->GetDescriptorIndex(&texture, SubresourceType::SRV, i); desc.width = std::max(1u, desc.width / 2); @@ -8419,10 +8439,17 @@ void GenerateMipChain(const Texture& texture, MIPGENFILTER filter, CommandList c 6, cmd); - GPUBarrier barriers[] = { - GPUBarrier::Memory(), - }; - device->Barrier(barriers, arraysize(barriers), cmd); + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, 0), + GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, 1), + GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, 2), + GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, 3), + GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, 4), + GPUBarrier::Image(&texture, ResourceState::UNORDERED_ACCESS, texture.desc.layout, i + 1, 5), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } } } @@ -8489,7 +8516,6 @@ void GenerateMipChain(const Texture& texture, MIPGENFILTER filter, CommandList c { GPUBarrier barriers[] = { - GPUBarrier::Memory(), GPUBarrier::Image(&texture,ResourceState::UNORDERED_ACCESS,texture.desc.layout,i + 1), }; device->Barrier(barriers, arraysize(barriers), cmd); @@ -8552,7 +8578,6 @@ void GenerateMipChain(const Texture& texture, MIPGENFILTER filter, CommandList c { GPUBarrier barriers[] = { - GPUBarrier::Memory(), GPUBarrier::Image(&texture,ResourceState::UNORDERED_ACCESS,texture.desc.layout,i + 1), }; device->Barrier(barriers, arraysize(barriers), cmd); @@ -8568,6 +8593,102 @@ void GenerateMipChain(const Texture& texture, MIPGENFILTER filter, CommandList c } } +void BlockCompress(const wi::graphics::Texture& texture_src, const wi::graphics::Texture& texture_bc, wi::graphics::CommandList cmd) +{ + const uint32_t block_size = GetFormatBlockSize(texture_bc.desc.format); + TextureDesc desc; + desc.width = std::max(1u, texture_bc.desc.width / block_size); + desc.height = std::max(1u, texture_bc.desc.height / block_size); + desc.bind_flags = BindFlag::UNORDERED_ACCESS; + desc.layout = ResourceState::UNORDERED_ACCESS; + desc.mip_levels = GetMipCount(desc.width, desc.height); // full mipchain + + static Texture bc_raw_uint2; + static Texture bc_raw_uint4; + Texture* bc_raw = nullptr; + + switch (texture_bc.desc.format) + { + case Format::BC1_UNORM: + case Format::BC1_UNORM_SRGB: + bc_raw = &bc_raw_uint2; + desc.format = Format::R32G32_UINT; + device->BindComputeShader(&shaders[CSTYPE_BLOCKCOMPRESS_BC1], cmd); + break; + case Format::BC3_UNORM: + case Format::BC3_UNORM_SRGB: + bc_raw = &bc_raw_uint4; + desc.format = Format::R32G32B32A32_UINT; + device->BindComputeShader(&shaders[CSTYPE_BLOCKCOMPRESS_BC3], cmd); + break; + case Format::BC5_UNORM: + bc_raw = &bc_raw_uint4; + desc.format = Format::R32G32B32A32_UINT; + device->BindComputeShader(&shaders[CSTYPE_BLOCKCOMPRESS_BC5], cmd); + break; + default: + assert(0); // not supported + return; + } + + if (!bc_raw->IsValid() || bc_raw->desc.width < desc.width || bc_raw->desc.height < desc.height) + { + device->CreateTexture(&desc, nullptr, bc_raw); + device->SetName(bc_raw, "bc_raw"); + + for (uint32_t i = 0; i < bc_raw->desc.mip_levels; ++i) + { + int subresource_index = device->CreateSubresource(bc_raw, SubresourceType::UAV, 0, 1, i, 1); + assert(subresource_index == i); + } + } + + device->EventBegin("BlockCompress", cmd); + + for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) + { + const uint32_t width = std::max(1u, desc.width >> mip); + const uint32_t height = std::max(1u, desc.height >> mip); + device->BindResource(&texture_src, 0, cmd, mip); + device->BindUAV(bc_raw, 0, cmd, mip); + device->Dispatch((width + 7u) / 8u, (height + 7u) / 8u, 1, cmd); + } + + GPUBarrier barriers[] = { + GPUBarrier::Image(bc_raw, ResourceState::UNORDERED_ACCESS, ResourceState::COPY_SRC), + GPUBarrier::Image(&texture_bc, texture_bc.desc.layout, ResourceState::COPY_DST), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + + for (uint32_t mip = 0; mip < texture_bc.desc.mip_levels; ++mip) + { + const uint32_t width = std::max(1u, desc.width >> mip); + const uint32_t height = std::max(1u, desc.height >> mip); + Box box; + box.left = 0; + box.right = width; + box.top = 0; + box.bottom = height; + box.front = 0; + box.back = 1; + + device->CopyTexture( + &texture_bc, 0, 0, 0, mip, 0, + bc_raw, std::min(mip, bc_raw->desc.mip_levels - 1), 0, + cmd, + &box + ); + } + + for (int i = 0; i < arraysize(barriers); ++i) + { + std::swap(barriers[i].image.layout_before, barriers[i].image.layout_after); + } + device->Barrier(barriers, arraysize(barriers), cmd); + + device->EventEnd(cmd); +} + void CopyTexture2D(const Texture& dst, int DstMIP, int DstX, int DstY, const Texture& src, int SrcMIP, CommandList cmd, BORDEREXPANDSTYLE borderExpand) { const TextureDesc& desc_dst = dst.GetDesc(); @@ -8627,10 +8748,8 @@ void CopyTexture2D(const Texture& dst, int DstMIP, int DstX, int DstY, const Tex device->Dispatch((cb.xCopySrcSize.x + 7) / 8, (cb.xCopySrcSize.y + 7) / 8, 1, cmd); - { GPUBarrier barriers[] = { - GPUBarrier::Memory(), GPUBarrier::Image(&dst,ResourceState::UNORDERED_ACCESS,dst.desc.layout, DstMIP), }; device->Barrier(barriers, arraysize(barriers), cmd); @@ -15440,6 +15559,12 @@ void AddDeferredMIPGen(const Texture& texture, bool preserve_coverage) deferredMIPGens.push_back(std::make_pair(texture, preserve_coverage)); deferredMIPGenLock.unlock(); } +void AddDeferredBlockCompression(const wi::graphics::Texture& texture_src, const wi::graphics::Texture& texture_bc) +{ + deferredMIPGenLock.lock(); + deferredBCQueue.push_back(std::make_pair(texture_src, texture_bc)); + deferredMIPGenLock.unlock(); +} diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index 9ae62534f..b88d73993 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -217,8 +217,8 @@ namespace wi::renderer uint32_t flags = DRAWSCENE_OPAQUE ); - // Render mip levels for textures that reqested it: - void ProcessDeferredMipGenRequests(wi::graphics::CommandList cmd); + // Process deferred requests such as AddDeferredMIPGen and AddDeferredBlockCompression: + void ProcessDeferredTextureRequests(wi::graphics::CommandList cmd); // Compute volumetric cloud shadow data void ComputeVolumetricCloudShadows( @@ -908,6 +908,11 @@ namespace wi::renderer }; void GenerateMipChain(const wi::graphics::Texture& texture, MIPGENFILTER filter, wi::graphics::CommandList cmd, const MIPGEN_OPTIONS& options = {}); + // Compress a texture into Block Compressed format + // texture_src : source uncompressed texture + // texture_bc : edstination comporessed texture, must be a supported BC format (BC1/BC3/BC5) + void BlockCompress(const wi::graphics::Texture& texture_src, const wi::graphics::Texture& texture_bc, wi::graphics::CommandList cmd); + enum BORDEREXPANDSTYLE { BORDEREXPAND_DISABLE, @@ -1093,6 +1098,7 @@ namespace wi::renderer // Add a texture that should be mipmapped whenever it is feasible to do so void AddDeferredMIPGen(const wi::graphics::Texture& texture, bool preserve_coverage = false); + void AddDeferredBlockCompression(const wi::graphics::Texture& texture_src, const wi::graphics::Texture& texture_bc); struct CustomShader { diff --git a/WickedEngine/wiResourceManager.cpp b/WickedEngine/wiResourceManager.cpp index e68be9036..31d4146b7 100644 --- a/WickedEngine/wiResourceManager.cpp +++ b/WickedEngine/wiResourceManager.cpp @@ -116,6 +116,16 @@ namespace wi resourceinternal->video = video; } + void Resource::SetOutdated() + { + if (internal_state == nullptr) + { + internal_state = std::make_shared(); + } + ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get(); + resourceinternal->flags |= resourcemanager::Flags::IMPORT_DELAY; // this will cause resource to be recreated, but let using old file data like delayed loading + } + namespace resourcemanager { static std::mutex locker; @@ -225,19 +235,28 @@ namespace wi { resource = std::make_shared(); resources[name] = resource; - locker.unlock(); } else { - locker.unlock(); - Resource retVal; - retVal.internal_state = resource; - return retVal; + if (!has_flag(flags, Flags::IMPORT_DELAY) && has_flag(resource->flags, Flags::IMPORT_DELAY)) + { + // If this is not an IMPORT_DELAY load, but this resource load was incomplete, using IMPORT_DELAY, + // then continue loading it as normal from existing file data and remove IMPORT_DELAY flag from it + resource->flags &= ~Flags::IMPORT_DELAY; + } + else + { + Resource retVal; + retVal.internal_state = resource; + locker.unlock(); + return retVal; + } } + locker.unlock(); if (filedata == nullptr || filesize == 0) { - if (!wi::helper::FileRead(name, resource->filedata)) + if (resource->filedata.empty() && !wi::helper::FileRead(name, resource->filedata)) { resource.reset(); return Resource(); @@ -246,138 +265,567 @@ namespace wi filesize = resource->filedata.size(); } - std::string ext = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(name)); - DataType type; - - // dynamic type selection: - { - auto it = types.find(ext); - if (it != types.end()) - { - type = it->second; - } - else - { - return Resource(); - } - } - bool success = false; - switch (type) + if (has_flag(flags, Flags::IMPORT_DELAY)) { - case DataType::IMAGE: + success = true; + } + else { - GraphicsDevice* device = wi::graphics::GetDevice(); - if (!ext.compare("KTX2")) + std::string ext = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(name)); + DataType type; + + // dynamic type selection: { - basist::ktx2_transcoder transcoder(&g_basis_global_codebook); - if (transcoder.init(filedata, (uint32_t)filesize)) + auto it = types.find(ext); + if (it != types.end()) { - TextureDesc desc; - desc.bind_flags = BindFlag::SHADER_RESOURCE; - desc.width = transcoder.get_width(); - desc.height = transcoder.get_height(); - desc.array_size = std::max(desc.array_size, transcoder.get_layers() * transcoder.get_faces()); - desc.mip_levels = transcoder.get_levels(); - desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; - if (transcoder.get_faces() == 6) - { - desc.misc_flags |= ResourceMiscFlag::TEXTURECUBE; - } + type = it->second; + } + else + { + return Resource(); + } + } - basist::transcoder_texture_format fmt; - if (has_flag(flags, Flags::IMPORT_NORMALMAP)) + switch (type) + { + case DataType::IMAGE: + { + GraphicsDevice* device = wi::graphics::GetDevice(); + if (!ext.compare("KTX2")) + { + basist::ktx2_transcoder transcoder(&g_basis_global_codebook); + if (transcoder.init(filedata, (uint32_t)filesize)) { - fmt = basist::transcoder_texture_format::cTFBC5_RG; - desc.format = Format::BC5_UNORM; - } - else - { - if (transcoder.get_has_alpha()) + TextureDesc desc; + desc.bind_flags = BindFlag::SHADER_RESOURCE; + desc.width = transcoder.get_width(); + desc.height = transcoder.get_height(); + desc.array_size = std::max(desc.array_size, transcoder.get_layers() * transcoder.get_faces()); + desc.mip_levels = transcoder.get_levels(); + desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; + if (transcoder.get_faces() == 6) { - fmt = basist::transcoder_texture_format::cTFBC3_RGBA; - desc.format = Format::BC3_UNORM; + desc.misc_flags |= ResourceMiscFlag::TEXTURECUBE; } - else - { - fmt = basist::transcoder_texture_format::cTFBC1_RGB; - desc.format = Format::BC1_UNORM; - } - } - uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(fmt); - if (transcoder.start_transcoding()) - { - // all subresources will use one allocation for transcoder destination, so compute combined size: - size_t transcoded_data_size = 0; - const uint32_t layers = std::max(1u, transcoder.get_layers()); - const uint32_t faces = transcoder.get_faces(); - const uint32_t levels = transcoder.get_levels(); - for (uint32_t layer = 0; layer < layers; ++layer) + basist::transcoder_texture_format fmt = basist::transcoder_texture_format::cTFRGBA32; + desc.format = Format::R8G8B8A8_UNORM; + + if (has_flag(flags, Flags::IMPORT_BLOCK_COMPRESSED)) { - for (uint32_t face = 0; face < faces; ++face) + if (has_flag(flags, Flags::IMPORT_NORMALMAP)) { - for (uint32_t mip = 0; mip < levels; ++mip) + fmt = basist::transcoder_texture_format::cTFBC5_RG; + desc.format = Format::BC5_UNORM; + } + else + { + if (transcoder.get_has_alpha()) { - basist::ktx2_image_level_info level_info; - if (transcoder.get_image_level_info(level_info, mip, layer, face)) - { - transcoded_data_size += level_info.m_total_blocks * bytes_per_block; - } + fmt = basist::transcoder_texture_format::cTFBC3_RGBA; + desc.format = Format::BC3_UNORM; + } + else + { + fmt = basist::transcoder_texture_format::cTFBC1_RGB; + desc.format = Format::BC1_UNORM; } } } - wi::vector transcoded_data(transcoded_data_size); + uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(fmt); - wi::vector InitData; - size_t transcoded_data_offset = 0; - for (uint32_t layer = 0; layer < layers; ++layer) + if (transcoder.start_transcoding()) { - for (uint32_t face = 0; face < faces; ++face) + // all subresources will use one allocation for transcoder destination, so compute combined size: + size_t transcoded_data_size = 0; + const uint32_t layers = std::max(1u, transcoder.get_layers()); + const uint32_t faces = transcoder.get_faces(); + const uint32_t levels = transcoder.get_levels(); + for (uint32_t layer = 0; layer < layers; ++layer) { - for (uint32_t mip = 0; mip < levels; ++mip) + for (uint32_t face = 0; face < faces; ++face) { - basist::ktx2_image_level_info level_info; - if (transcoder.get_image_level_info(level_info, mip, layer, face)) + for (uint32_t mip = 0; mip < levels; ++mip) { - void* data_ptr = transcoded_data.data() + transcoded_data_offset; - transcoded_data_offset += level_info.m_total_blocks * bytes_per_block; - if (transcoder.transcode_image_level( - mip, - layer, - face, - data_ptr, - level_info.m_total_blocks, - fmt - )) + basist::ktx2_image_level_info level_info; + if (transcoder.get_image_level_info(level_info, mip, layer, face)) { - SubresourceData subresourceData; - subresourceData.data_ptr = data_ptr; - subresourceData.row_pitch = level_info.m_num_blocks_x * bytes_per_block; - subresourceData.slice_pitch = subresourceData.row_pitch * level_info.m_num_blocks_y; - InitData.push_back(subresourceData); + transcoded_data_size += level_info.m_total_blocks * bytes_per_block; + } + } + } + } + wi::vector transcoded_data(transcoded_data_size); + + wi::vector InitData; + size_t transcoded_data_offset = 0; + for (uint32_t layer = 0; layer < layers; ++layer) + { + for (uint32_t face = 0; face < faces; ++face) + { + for (uint32_t mip = 0; mip < levels; ++mip) + { + basist::ktx2_image_level_info level_info; + if (transcoder.get_image_level_info(level_info, mip, layer, face)) + { + void* data_ptr = transcoded_data.data() + transcoded_data_offset; + transcoded_data_offset += level_info.m_total_blocks * bytes_per_block; + if (transcoder.transcode_image_level( + mip, + layer, + face, + data_ptr, + level_info.m_total_blocks, + fmt + )) + { + SubresourceData subresourceData; + subresourceData.data_ptr = data_ptr; + subresourceData.row_pitch = level_info.m_num_blocks_x * bytes_per_block; + subresourceData.slice_pitch = subresourceData.row_pitch * level_info.m_num_blocks_y; + InitData.push_back(subresourceData); + } + else + { + wi::backlog::post("KTX2 transcoding error while loading image!", wi::backlog::LogLevel::Error); + assert(0); + } } else { - wi::backlog::post("KTX2 transcoding error while loading image!", wi::backlog::LogLevel::Error); + wi::backlog::post("KTX2 transcoding error while loading image level info!", wi::backlog::LogLevel::Error); assert(0); } } + } + } + + if (!InitData.empty()) + { + success = device->CreateTexture(&desc, InitData.data(), &resource->texture); + device->SetName(&resource->texture, name.c_str()); + + Format srgb_format = GetFormatSRGB(desc.format); + if (srgb_format != Format::UNKNOWN && srgb_format != desc.format) + { + resource->srgb_subresource = device->CreateSubresource( + &resource->texture, + SubresourceType::SRV, + 0, -1, + 0, -1, + &srgb_format + ); + } + } + } + transcoder.clear(); + } + } + else if (!ext.compare("BASIS")) + { + basist::basisu_transcoder transcoder(&g_basis_global_codebook); + if (transcoder.validate_header(filedata, (uint32_t)filesize)) + { + basist::basisu_file_info fileInfo; + if (transcoder.get_file_info(filedata, (uint32_t)filesize, fileInfo)) + { + uint32_t image_index = 0; + basist::basisu_image_info info; + if (transcoder.get_image_info(filedata, (uint32_t)filesize, info, image_index)) + { + TextureDesc desc; + desc.bind_flags = BindFlag::SHADER_RESOURCE; + desc.width = info.m_width; + desc.height = info.m_height; + desc.mip_levels = info.m_total_levels; + desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; + + basist::transcoder_texture_format fmt; + if (has_flag(flags, Flags::IMPORT_NORMALMAP)) + { + fmt = basist::transcoder_texture_format::cTFBC5_RG; + desc.format = Format::BC5_UNORM; + } + else + { + if (info.m_alpha_flag) + { + fmt = basist::transcoder_texture_format::cTFBC3_RGBA; + desc.format = Format::BC3_UNORM; + } else { - wi::backlog::post("KTX2 transcoding error while loading image level info!", wi::backlog::LogLevel::Error); - assert(0); + fmt = basist::transcoder_texture_format::cTFBC1_RGB; + desc.format = Format::BC1_UNORM; + } + } + uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(fmt); + + if (transcoder.start_transcoding(filedata, (uint32_t)filesize)) + { + // all subresources will use one allocation for transcoder destination, so compute combined size: + size_t transcoded_data_size = 0; + for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) + { + basist::basisu_image_level_info level_info; + if (transcoder.get_image_level_info(filedata, (uint32_t)filesize, level_info, image_index, mip)) + { + transcoded_data_size += level_info.m_total_blocks * bytes_per_block; + } + } + wi::vector transcoded_data(transcoded_data_size); + + wi::vector InitData; + size_t transcoded_data_offset = 0; + for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) + { + basist::basisu_image_level_info level_info; + if (transcoder.get_image_level_info(filedata, (uint32_t)filesize, level_info, 0, mip)) + { + void* data_ptr = transcoded_data.data() + transcoded_data_offset; + transcoded_data_offset += level_info.m_total_blocks * bytes_per_block; + if (transcoder.transcode_image_level( + filedata, + (uint32_t)filesize, + image_index, + mip, + data_ptr, + level_info.m_total_blocks, + fmt + )) + { + SubresourceData subresourceData; + subresourceData.data_ptr = data_ptr; + subresourceData.row_pitch = level_info.m_num_blocks_x * bytes_per_block; + subresourceData.slice_pitch = subresourceData.row_pitch * level_info.m_num_blocks_y; + InitData.push_back(subresourceData); + } + else + { + wi::backlog::post("BASIS transcoding error while loading image!", wi::backlog::LogLevel::Error); + assert(0); + } + } + else + { + wi::backlog::post("BASIS transcoding error while loading image level info!", wi::backlog::LogLevel::Error); + assert(0); + } + } + + if (!InitData.empty()) + { + success = device->CreateTexture(&desc, InitData.data(), &resource->texture); + device->SetName(&resource->texture, name.c_str()); + + Format srgb_format = GetFormatSRGB(desc.format); + if (srgb_format != Format::UNKNOWN && srgb_format != desc.format) + { + resource->srgb_subresource = device->CreateSubresource( + &resource->texture, + SubresourceType::SRV, + 0, -1, + 0, -1, + &srgb_format + ); + } } } } } + } + } + else if (!ext.compare("DDS")) + { + // Load dds - if (!InitData.empty()) + tinyddsloader::DDSFile dds; + auto result = dds.Load(filedata, filesize); + + if (result == tinyddsloader::Result::Success) + { + TextureDesc desc; + desc.array_size = 1; + desc.bind_flags = BindFlag::SHADER_RESOURCE; + desc.width = dds.GetWidth(); + desc.height = dds.GetHeight(); + desc.depth = dds.GetDepth(); + desc.mip_levels = dds.GetMipCount(); + desc.array_size = dds.GetArraySize(); + desc.format = Format::R8G8B8A8_UNORM; + desc.layout = ResourceState::SHADER_RESOURCE; + desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; + + if (dds.IsCubemap()) { - success = device->CreateTexture(&desc, InitData.data(), &resource->texture); + desc.misc_flags |= ResourceMiscFlag::TEXTURECUBE; + } + + auto ddsFormat = dds.GetFormat(); + + switch (ddsFormat) + { + case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_Float: desc.format = Format::R32G32B32A32_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_UInt: desc.format = Format::R32G32B32A32_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_SInt: desc.format = Format::R32G32B32A32_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_Float: desc.format = Format::R32G32B32_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_UInt: desc.format = Format::R32G32B32_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_SInt: desc.format = Format::R32G32B32_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_Float: desc.format = Format::R16G16B16A16_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_UNorm: desc.format = Format::R16G16B16A16_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_UInt: desc.format = Format::R16G16B16A16_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_SNorm: desc.format = Format::R16G16B16A16_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_SInt: desc.format = Format::R16G16B16A16_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32_Float: desc.format = Format::R32G32_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32_UInt: desc.format = Format::R32G32_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32G32_SInt: desc.format = Format::R32G32_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R10G10B10A2_UNorm: desc.format = Format::R10G10B10A2_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R10G10B10A2_UInt: desc.format = Format::R10G10B10A2_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R11G11B10_Float: desc.format = Format::R11G11B10_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::B8G8R8X8_UNorm: desc.format = Format::B8G8R8A8_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm: desc.format = Format::B8G8R8A8_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm_SRGB: desc.format = Format::B8G8R8A8_UNORM_SRGB; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm: desc.format = Format::R8G8B8A8_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm_SRGB: desc.format = Format::R8G8B8A8_UNORM_SRGB; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UInt: desc.format = Format::R8G8B8A8_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SNorm: desc.format = Format::R8G8B8A8_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SInt: desc.format = Format::R8G8B8A8_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16_Float: desc.format = Format::R16G16_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16_UNorm: desc.format = Format::R16G16_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16_UInt: desc.format = Format::R16G16_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16_SNorm: desc.format = Format::R16G16_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R16G16_SInt: desc.format = Format::R16G16_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::D32_Float: desc.format = Format::D32_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32_Float: desc.format = Format::R32_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32_UInt: desc.format = Format::R32_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R32_SInt: desc.format = Format::R32_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8_UNorm: desc.format = Format::R8G8_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8_UInt: desc.format = Format::R8G8_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8_SNorm: desc.format = Format::R8G8_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R8G8_SInt: desc.format = Format::R8G8_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16_Float: desc.format = Format::R16_FLOAT; break; + case tinyddsloader::DDSFile::DXGIFormat::D16_UNorm: desc.format = Format::D16_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R16_UNorm: desc.format = Format::R16_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R16_UInt: desc.format = Format::R16_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R16_SNorm: desc.format = Format::R16_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R16_SInt: desc.format = Format::R16_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R8_UNorm: desc.format = Format::R8_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R8_UInt: desc.format = Format::R8_UINT; break; + case tinyddsloader::DDSFile::DXGIFormat::R8_SNorm: desc.format = Format::R8_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::R8_SInt: desc.format = Format::R8_SINT; break; + case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm: desc.format = Format::BC1_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm_SRGB: desc.format = Format::BC1_UNORM_SRGB; break; + case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm: desc.format = Format::BC2_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm_SRGB: desc.format = Format::BC2_UNORM_SRGB; break; + case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm: desc.format = Format::BC3_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm_SRGB: desc.format = Format::BC3_UNORM_SRGB; break; + case tinyddsloader::DDSFile::DXGIFormat::BC4_UNorm: desc.format = Format::BC4_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC4_SNorm: desc.format = Format::BC4_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC5_UNorm: desc.format = Format::BC5_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC5_SNorm: desc.format = Format::BC5_SNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC6H_SF16: desc.format = Format::BC6H_SF16; break; + case tinyddsloader::DDSFile::DXGIFormat::BC6H_UF16: desc.format = Format::BC6H_UF16; break; + case tinyddsloader::DDSFile::DXGIFormat::BC7_UNorm: desc.format = Format::BC7_UNORM; break; + case tinyddsloader::DDSFile::DXGIFormat::BC7_UNorm_SRGB: desc.format = Format::BC7_UNORM_SRGB; break; + default: + assert(0); // incoming format is not supported + break; + } + + wi::vector InitData; + for (uint32_t arrayIndex = 0; arrayIndex < desc.array_size; ++arrayIndex) + { + for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) + { + auto imageData = dds.GetImageData(mip, arrayIndex); + SubresourceData subresourceData; + subresourceData.data_ptr = imageData->m_mem; + subresourceData.row_pitch = imageData->m_memPitch; + subresourceData.slice_pitch = imageData->m_memSlicePitch; + InitData.push_back(subresourceData); + } + } + + auto dim = dds.GetTextureDimension(); + switch (dim) + { + case tinyddsloader::DDSFile::TextureDimension::Texture1D: + { + desc.type = TextureDesc::Type::TEXTURE_1D; + } + break; + case tinyddsloader::DDSFile::TextureDimension::Texture2D: + { + desc.type = TextureDesc::Type::TEXTURE_2D; + } + break; + case tinyddsloader::DDSFile::TextureDimension::Texture3D: + { + desc.type = TextureDesc::Type::TEXTURE_3D; + } + break; + default: + assert(0); + break; + } + + if (IsFormatBlockCompressed(desc.format)) + { + desc.width = AlignTo(desc.width, GetFormatBlockSize(desc.format)); + desc.height = AlignTo(desc.height, GetFormatBlockSize(desc.format)); + } + + success = device->CreateTexture(&desc, InitData.data(), &resource->texture); + device->SetName(&resource->texture, name.c_str()); + + Format srgb_format = GetFormatSRGB(desc.format); + if (srgb_format != Format::UNKNOWN && srgb_format != desc.format) + { + resource->srgb_subresource = device->CreateSubresource( + &resource->texture, + SubresourceType::SRV, + 0, -1, + 0, -1, + &srgb_format + ); + } + } + else assert(0); // failed to load DDS + + } + else if (!ext.compare("HDR")) + { + int height, width, channels; // stb_image + float* data = stbi_loadf_from_memory(filedata, (int)filesize, &width, &height, &channels, 0); + + if (data != nullptr) + { + TextureDesc desc; + desc.width = (uint32_t)width; + desc.height = (uint32_t)height; + switch (channels) + { + default: + case 4: + desc.format = Format::R32G32B32A32_FLOAT; + break; + case 3: + desc.format = Format::R32G32B32_FLOAT; + break; + case 2: + desc.format = Format::R32G32_FLOAT; + break; + case 1: + desc.format = Format::R32_FLOAT; + break; + } + desc.bind_flags = BindFlag::SHADER_RESOURCE; + desc.mip_levels = 1; + SubresourceData InitData; + InitData.data_ptr = data; + InitData.row_pitch = width * GetFormatStride(desc.format); + success = device->CreateTexture(&desc, &InitData, &resource->texture); + device->SetName(&resource->texture, name.c_str()); + } + } + else + { + // qoi, png, tga, jpg, etc. loader: + + const int channelCount = 4; + int height, width, bpp; // stb_image + qoi_desc desc; + + void* rgba; + if (!ext.compare("QOI")) + { + rgba = qoi_decode(filedata, (int)filesize, &desc, channelCount); + // redefine width, height to avoid further conditionals + height = desc.height; + width = desc.width; + } + else + rgba = stbi_load_from_memory(filedata, (int)filesize, &width, &height, &bpp, channelCount); + + if (rgba != nullptr) + { + TextureDesc desc; + desc.height = uint32_t(height); + desc.width = uint32_t(width); + desc.layout = ResourceState::SHADER_RESOURCE; + + if (has_flag(flags, Flags::IMPORT_COLORGRADINGLUT)) + { + if (desc.type != TextureDesc::Type::TEXTURE_2D || + desc.width != 256 || + desc.height != 16) + { + wi::helper::messageBox("The Dimensions must be 256 x 16 for color grading LUT!", "Error"); + } + else + { + uint32_t data[16 * 16 * 16]; + int pixel = 0; + for (int z = 0; z < 16; ++z) + { + for (int y = 0; y < 16; ++y) + { + for (int x = 0; x < 16; ++x) + { + int coord = x + y * 256 + z * 16; + data[pixel++] = ((uint32_t*)rgba)[coord]; + } + } + } + + desc.type = TextureDesc::Type::TEXTURE_3D; + desc.width = 16; + desc.height = 16; + desc.depth = 16; + desc.format = Format::R8G8B8A8_UNORM; + desc.bind_flags = BindFlag::SHADER_RESOURCE; + SubresourceData InitData; + InitData.data_ptr = data; + InitData.row_pitch = 16 * sizeof(uint32_t); + InitData.slice_pitch = 16 * InitData.row_pitch; + success = device->CreateTexture(&desc, &InitData, &resource->texture); + device->SetName(&resource->texture, name.c_str()); + } + } + else + { + desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; + desc.format = Format::R8G8B8A8_UNORM; + desc.mip_levels = GetMipCount(desc.width, desc.height); + desc.usage = Usage::DEFAULT; + desc.layout = ResourceState::SHADER_RESOURCE; + desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; + + uint32_t mipwidth = width; + SubresourceData init_data[32]; // don't support more than 32 mips anyway + for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) + { + init_data[mip].data_ptr = rgba; // attention! we don't fill the mips here correctly, just always point to the mip0 data by default. Mip levels will be created using compute shader when needed! + init_data[mip].row_pitch = static_cast(mipwidth * channelCount); + mipwidth = std::max(1u, mipwidth / 2); + } + + success = device->CreateTexture(&desc, init_data, &resource->texture); device->SetName(&resource->texture, name.c_str()); + for (uint32_t i = 0; i < resource->texture.desc.mip_levels; ++i) + { + int subresource_index; + subresource_index = device->CreateSubresource(&resource->texture, SubresourceType::SRV, 0, 1, i, 1); + assert(subresource_index == i); + subresource_index = device->CreateSubresource(&resource->texture, SubresourceType::UAV, 0, 1, i, 1); + assert(subresource_index == i); + } + + // This part must be AFTER mip level subresource creation: Format srgb_format = GetFormatSRGB(desc.format); if (srgb_format != Format::UNKNOWN && srgb_format != desc.format) { @@ -389,461 +837,100 @@ namespace wi &srgb_format ); } - } - } - transcoder.clear(); - } - } - else if (!ext.compare("BASIS")) - { - basist::basisu_transcoder transcoder(&g_basis_global_codebook); - if (transcoder.validate_header(filedata, (uint32_t)filesize)) - { - basist::basisu_file_info fileInfo; - if (transcoder.get_file_info(filedata, (uint32_t)filesize, fileInfo)) - { - uint32_t image_index = 0; - basist::basisu_image_info info; - if (transcoder.get_image_info(filedata, (uint32_t)filesize, info, image_index)) - { - TextureDesc desc; - desc.bind_flags = BindFlag::SHADER_RESOURCE; - desc.width = info.m_width; - desc.height = info.m_height; - desc.mip_levels = info.m_total_levels; - desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; - basist::transcoder_texture_format fmt; - if (has_flag(flags, Flags::IMPORT_NORMALMAP)) + wi::renderer::AddDeferredMIPGen(resource->texture, true); + + if (has_flag(flags, Flags::IMPORT_BLOCK_COMPRESSED)) { - fmt = basist::transcoder_texture_format::cTFBC5_RG; - desc.format = Format::BC5_UNORM; - } - else - { - if (info.m_alpha_flag) + // Schedul additional task to compress into BC format and replace resource texture: + Texture uncompressed_src = std::move(resource->texture); + resource->srgb_subresource = -1; + + desc.width = AlignTo(desc.width, GetFormatBlockSize(desc.format)); + desc.height = AlignTo(desc.height, GetFormatBlockSize(desc.format)); + + desc.format = Format::BC1_UNORM; + if (has_flag(flags, Flags::IMPORT_NORMALMAP)) { - fmt = basist::transcoder_texture_format::cTFBC3_RGBA; - desc.format = Format::BC3_UNORM; + desc.format = Format::BC5_UNORM; } else { - fmt = basist::transcoder_texture_format::cTFBC1_RGB; - desc.format = Format::BC1_UNORM; - } - } - uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(fmt); - - if (transcoder.start_transcoding(filedata, (uint32_t)filesize)) - { - // all subresources will use one allocation for transcoder destination, so compute combined size: - size_t transcoded_data_size = 0; - for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) - { - basist::basisu_image_level_info level_info; - if (transcoder.get_image_level_info(filedata, (uint32_t)filesize, level_info, image_index, mip)) + // scan for transparency: + // By default we should use BC1 that doesn't have transparency, but half the size of BC3 that supports it + bool has_transparency = false; + for (int y = 0; (y < height) && !has_transparency; ++y) { - transcoded_data_size += level_info.m_total_blocks * bytes_per_block; - } - } - wi::vector transcoded_data(transcoded_data_size); - - wi::vector InitData; - size_t transcoded_data_offset = 0; - for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) - { - basist::basisu_image_level_info level_info; - if (transcoder.get_image_level_info(filedata, (uint32_t)filesize, level_info, 0, mip)) - { - void* data_ptr = transcoded_data.data() + transcoded_data_offset; - transcoded_data_offset += level_info.m_total_blocks * bytes_per_block; - if (transcoder.transcode_image_level( - filedata, - (uint32_t)filesize, - image_index, - mip, - data_ptr, - level_info.m_total_blocks, - fmt - )) + for (int x = 0; (x < width) && !has_transparency; ++x) { - SubresourceData subresourceData; - subresourceData.data_ptr = data_ptr; - subresourceData.row_pitch = level_info.m_num_blocks_x * bytes_per_block; - subresourceData.slice_pitch = subresourceData.row_pitch * level_info.m_num_blocks_y; - InitData.push_back(subresourceData); - } - else - { - wi::backlog::post("BASIS transcoding error while loading image!", wi::backlog::LogLevel::Error); - assert(0); + wi::Color color = ((wi::Color*)rgba)[x + y * width]; + has_transparency |= color.getA() < 255; } } - else + if (has_transparency) { - wi::backlog::post("BASIS transcoding error while loading image level info!", wi::backlog::LogLevel::Error); - assert(0); + // BC3 format supports transparency: + desc.format = Format::BC3_UNORM; } } + desc.bind_flags = BindFlag::SHADER_RESOURCE; - if (!InitData.empty()) + success = device->CreateTexture(&desc, nullptr, &resource->texture); + device->SetName(&resource->texture, name.c_str()); + + // This part must be AFTER mip level subresource creation: + Format srgb_format = GetFormatSRGB(desc.format); + if (srgb_format != Format::UNKNOWN && srgb_format != desc.format) { - success = device->CreateTexture(&desc, InitData.data(), &resource->texture); - device->SetName(&resource->texture, name.c_str()); - - Format srgb_format = GetFormatSRGB(desc.format); - if (srgb_format != Format::UNKNOWN && srgb_format != desc.format) - { - resource->srgb_subresource = device->CreateSubresource( - &resource->texture, - SubresourceType::SRV, - 0, -1, - 0, -1, - &srgb_format - ); - } + resource->srgb_subresource = device->CreateSubresource( + &resource->texture, + SubresourceType::SRV, + 0, -1, + 0, -1, + &srgb_format + ); } + + wi::renderer::AddDeferredBlockCompression(uncompressed_src, resource->texture); } } } + free(rgba); } } - else if (!ext.compare("DDS")) + break; + + case DataType::SOUND: { - // Load dds - - tinyddsloader::DDSFile dds; - auto result = dds.Load(filedata, filesize); - - if (result == tinyddsloader::Result::Success) - { - TextureDesc desc; - desc.array_size = 1; - desc.bind_flags = BindFlag::SHADER_RESOURCE; - desc.width = dds.GetWidth(); - desc.height = dds.GetHeight(); - desc.depth = dds.GetDepth(); - desc.mip_levels = dds.GetMipCount(); - desc.array_size = dds.GetArraySize(); - desc.format = Format::R8G8B8A8_UNORM; - desc.layout = ResourceState::SHADER_RESOURCE; - desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; - - if (dds.IsCubemap()) - { - desc.misc_flags |= ResourceMiscFlag::TEXTURECUBE; - } - - auto ddsFormat = dds.GetFormat(); - - switch (ddsFormat) - { - case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_Float: desc.format = Format::R32G32B32A32_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_UInt: desc.format = Format::R32G32B32A32_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_SInt: desc.format = Format::R32G32B32A32_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_Float: desc.format = Format::R32G32B32_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_UInt: desc.format = Format::R32G32B32_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_SInt: desc.format = Format::R32G32B32_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_Float: desc.format = Format::R16G16B16A16_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_UNorm: desc.format = Format::R16G16B16A16_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_UInt: desc.format = Format::R16G16B16A16_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_SNorm: desc.format = Format::R16G16B16A16_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_SInt: desc.format = Format::R16G16B16A16_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32_Float: desc.format = Format::R32G32_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32_UInt: desc.format = Format::R32G32_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32G32_SInt: desc.format = Format::R32G32_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R10G10B10A2_UNorm: desc.format = Format::R10G10B10A2_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R10G10B10A2_UInt: desc.format = Format::R10G10B10A2_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R11G11B10_Float: desc.format = Format::R11G11B10_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::B8G8R8X8_UNorm: desc.format = Format::B8G8R8A8_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm: desc.format = Format::B8G8R8A8_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm_SRGB: desc.format = Format::B8G8R8A8_UNORM_SRGB; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm: desc.format = Format::R8G8B8A8_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm_SRGB: desc.format = Format::R8G8B8A8_UNORM_SRGB; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UInt: desc.format = Format::R8G8B8A8_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SNorm: desc.format = Format::R8G8B8A8_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SInt: desc.format = Format::R8G8B8A8_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16_Float: desc.format = Format::R16G16_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16_UNorm: desc.format = Format::R16G16_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16_UInt: desc.format = Format::R16G16_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16_SNorm: desc.format = Format::R16G16_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R16G16_SInt: desc.format = Format::R16G16_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::D32_Float: desc.format = Format::D32_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32_Float: desc.format = Format::R32_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32_UInt: desc.format = Format::R32_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R32_SInt: desc.format = Format::R32_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8_UNorm: desc.format = Format::R8G8_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8_UInt: desc.format = Format::R8G8_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8_SNorm: desc.format = Format::R8G8_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R8G8_SInt: desc.format = Format::R8G8_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16_Float: desc.format = Format::R16_FLOAT; break; - case tinyddsloader::DDSFile::DXGIFormat::D16_UNorm: desc.format = Format::D16_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R16_UNorm: desc.format = Format::R16_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R16_UInt: desc.format = Format::R16_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R16_SNorm: desc.format = Format::R16_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R16_SInt: desc.format = Format::R16_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R8_UNorm: desc.format = Format::R8_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R8_UInt: desc.format = Format::R8_UINT; break; - case tinyddsloader::DDSFile::DXGIFormat::R8_SNorm: desc.format = Format::R8_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::R8_SInt: desc.format = Format::R8_SINT; break; - case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm: desc.format = Format::BC1_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm_SRGB: desc.format = Format::BC1_UNORM_SRGB; break; - case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm: desc.format = Format::BC2_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm_SRGB: desc.format = Format::BC2_UNORM_SRGB; break; - case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm: desc.format = Format::BC3_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm_SRGB: desc.format = Format::BC3_UNORM_SRGB; break; - case tinyddsloader::DDSFile::DXGIFormat::BC4_UNorm: desc.format = Format::BC4_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC4_SNorm: desc.format = Format::BC4_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC5_UNorm: desc.format = Format::BC5_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC5_SNorm: desc.format = Format::BC5_SNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC6H_SF16: desc.format = Format::BC6H_SF16; break; - case tinyddsloader::DDSFile::DXGIFormat::BC6H_UF16: desc.format = Format::BC6H_UF16; break; - case tinyddsloader::DDSFile::DXGIFormat::BC7_UNorm: desc.format = Format::BC7_UNORM; break; - case tinyddsloader::DDSFile::DXGIFormat::BC7_UNorm_SRGB: desc.format = Format::BC7_UNORM_SRGB; break; - default: - assert(0); // incoming format is not supported - break; - } - - wi::vector InitData; - for (uint32_t arrayIndex = 0; arrayIndex < desc.array_size; ++arrayIndex) - { - for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) - { - auto imageData = dds.GetImageData(mip, arrayIndex); - SubresourceData subresourceData; - subresourceData.data_ptr = imageData->m_mem; - subresourceData.row_pitch = imageData->m_memPitch; - subresourceData.slice_pitch = imageData->m_memSlicePitch; - InitData.push_back(subresourceData); - } - } - - auto dim = dds.GetTextureDimension(); - switch (dim) - { - case tinyddsloader::DDSFile::TextureDimension::Texture1D: - { - desc.type = TextureDesc::Type::TEXTURE_1D; - } - break; - case tinyddsloader::DDSFile::TextureDimension::Texture2D: - { - desc.type = TextureDesc::Type::TEXTURE_2D; - } - break; - case tinyddsloader::DDSFile::TextureDimension::Texture3D: - { - desc.type = TextureDesc::Type::TEXTURE_3D; - } - break; - default: - assert(0); - break; - } - - if (IsFormatBlockCompressed(desc.format)) - { - desc.width = AlignTo(desc.width, GetFormatBlockSize(desc.format)); - desc.height = AlignTo(desc.height, GetFormatBlockSize(desc.format)); - } - - success = device->CreateTexture(&desc, InitData.data(), &resource->texture); - device->SetName(&resource->texture, name.c_str()); - - Format srgb_format = GetFormatSRGB(desc.format); - if (srgb_format != Format::UNKNOWN && srgb_format != desc.format) - { - resource->srgb_subresource = device->CreateSubresource( - &resource->texture, - SubresourceType::SRV, - 0, -1, - 0, -1, - &srgb_format - ); - } - } - else assert(0); // failed to load DDS - + success = wi::audio::CreateSound(filedata, filesize, &resource->sound); } - else if(!ext.compare("HDR")) + break; + + case DataType::SCRIPT: { - int height, width, channels; // stb_image - float* data = stbi_loadf_from_memory(filedata, (int)filesize, &width, &height, &channels, 0); - - if (data != nullptr) - { - TextureDesc desc; - desc.width = (uint32_t)width; - desc.height = (uint32_t)height; - switch (channels) - { - default: - case 4: - desc.format = Format::R32G32B32A32_FLOAT; - break; - case 3: - desc.format = Format::R32G32B32_FLOAT; - break; - case 2: - desc.format = Format::R32G32_FLOAT; - break; - case 1: - desc.format = Format::R32_FLOAT; - break; - } - desc.bind_flags = BindFlag::SHADER_RESOURCE; - desc.mip_levels = 1; - SubresourceData InitData; - InitData.data_ptr = data; - InitData.row_pitch = width * GetFormatStride(desc.format); - success = device->CreateTexture(&desc, &InitData, &resource->texture); - device->SetName(&resource->texture, name.c_str()); - } + resource->script.resize(filesize); + std::memcpy(resource->script.data(), filedata, filesize); + success = true; } - else + break; + + case DataType::VIDEO: { - // qoi, png, tga, jpg, etc. loader: - - const int channelCount = 4; - int height, width, bpp; // stb_image - qoi_desc desc; - - void* rgb; - if (!ext.compare("QOI")) - { - rgb = qoi_decode(filedata, (int)filesize, &desc, channelCount); - // redefine width, height to avoid further conditionals - height = desc.height; - width = desc.width; - } - else - rgb = stbi_load_from_memory(filedata, (int)filesize, &width, &height, &bpp, channelCount); - - if (rgb != nullptr) - { - TextureDesc desc; - desc.height = uint32_t(height); - desc.width = uint32_t(width); - desc.layout = ResourceState::SHADER_RESOURCE; - - if (has_flag(flags, Flags::IMPORT_COLORGRADINGLUT)) - { - if (desc.type != TextureDesc::Type::TEXTURE_2D || - desc.width != 256 || - desc.height != 16) - { - wi::helper::messageBox("The Dimensions must be 256 x 16 for color grading LUT!", "Error"); - } - else - { - uint32_t data[16 * 16 * 16]; - int pixel = 0; - for (int z = 0; z < 16; ++z) - { - for (int y = 0; y < 16; ++y) - { - for (int x = 0; x < 16; ++x) - { - int coord = x + y * 256 + z * 16; - data[pixel++] = ((uint32_t*)rgb)[coord]; - } - } - } - - desc.type = TextureDesc::Type::TEXTURE_3D; - desc.width = 16; - desc.height = 16; - desc.depth = 16; - desc.format = Format::R8G8B8A8_UNORM; - desc.bind_flags = BindFlag::SHADER_RESOURCE; - SubresourceData InitData; - InitData.data_ptr = data; - InitData.row_pitch = 16 * sizeof(uint32_t); - InitData.slice_pitch = 16 * InitData.row_pitch; - success = device->CreateTexture(&desc, &InitData, &resource->texture); - device->SetName(&resource->texture, name.c_str()); - } - } - else - { - desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; - desc.format = Format::R8G8B8A8_UNORM; - desc.mip_levels = (uint32_t)log2(std::max(width, height)) + 1; - desc.usage = Usage::DEFAULT; - desc.layout = ResourceState::SHADER_RESOURCE; - desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING; - - uint32_t mipwidth = width; - SubresourceData init_data[32]; // don't support more than 32 mips anyway - for (uint32_t mip = 0; mip < desc.mip_levels; ++mip) - { - init_data[mip].data_ptr = rgb; // attention! we don't fill the mips here correctly, just always point to the mip0 data by default. Mip levels will be created using compute shader when needed! - init_data[mip].row_pitch = static_cast(mipwidth * channelCount); - mipwidth = std::max(1u, mipwidth / 2); - } - - success = device->CreateTexture(&desc, init_data, &resource->texture); - device->SetName(&resource->texture, name.c_str()); - - for (uint32_t i = 0; i < resource->texture.desc.mip_levels; ++i) - { - int subresource_index; - subresource_index = device->CreateSubresource(&resource->texture, SubresourceType::SRV, 0, 1, i, 1); - assert(subresource_index == i); - subresource_index = device->CreateSubresource(&resource->texture, SubresourceType::UAV, 0, 1, i, 1); - assert(subresource_index == i); - } - - // This part must be AFTER mip level subresource creation: - { - Format srgb_format = GetFormatSRGB(desc.format); - resource->srgb_subresource = device->CreateSubresource( - &resource->texture, - SubresourceType::SRV, - 0, -1, - 0, -1, - &srgb_format - ); - } - - wi::renderer::AddDeferredMIPGen(resource->texture, true); - } - } - free(rgb); + success = wi::video::CreateVideo(filedata, filesize, &resource->video); } - } - break; + break; - case DataType::SOUND: - { - success = wi::audio::CreateSound(filedata, filesize, &resource->sound); + }; } - break; - - case DataType::SCRIPT: - { - resource->script.resize(filesize); - std::memcpy(resource->script.data(), filedata, filesize); - success = true; - } - break; - - case DataType::VIDEO: - { - success = wi::video::CreateVideo(filedata, filesize, &resource->video); - } - break; - - }; if (success) { resource->flags = flags; - if (resource->filedata.empty() && has_flag(flags, Flags::IMPORT_RETAIN_FILEDATA)) + if (resource->filedata.empty() && (has_flag(flags, Flags::IMPORT_RETAIN_FILEDATA) || has_flag(flags, Flags::IMPORT_DELAY))) { // resource was loaded with external filedata, and we want to retain filedata + // this must also happen when using IMPORT_DELAY! resource->filedata.resize(filesize); std::memcpy(resource->filedata.data(), filedata, filesize); } @@ -912,6 +999,7 @@ namespace wi archive >> resource.filedata; resource.name = archive.GetSourceDirectory() + resource.name; + resource.flags |= Flags::IMPORT_DELAY; // delay resource creation, to be able to receive additional flags (this way only file data is loaded) // "Loading" the resource can happen asynchronously to serialization of file data, to improve performance wi::jobsystem::Execute(ctx, [i, &temp_resources, &seri_locker, &seri](wi::jobsystem::JobArgs args) { diff --git a/WickedEngine/wiResourceManager.h b/WickedEngine/wiResourceManager.h index f1cc1e101..56954b196 100644 --- a/WickedEngine/wiResourceManager.h +++ b/WickedEngine/wiResourceManager.h @@ -33,6 +33,10 @@ namespace wi void SetSound(const wi::audio::Sound& sound); void SetScript(const std::string& script); void SetVideo(const wi::video::Video& script); + + // Resource marked for recreate on resourcemanager::Load() + // It keeps embedded file data if exists + void SetOutdated(); }; namespace resourcemanager @@ -57,6 +61,8 @@ namespace wi IMPORT_COLORGRADINGLUT = 1 << 0, // image import will convert resource to 3D color grading LUT IMPORT_RETAIN_FILEDATA = 1 << 1, // file data will be kept for later reuse. This is necessary for keeping the resource serializable IMPORT_NORMALMAP = 1 << 2, // image import will try to use optimal normal map encoding + IMPORT_BLOCK_COMPRESSED = 1 << 3, // image import will request block compression for uncompressed or transcodable formats + IMPORT_DELAY = 1 << 4, // delay importing resource until later, for example when proper flags can be determined }; // Load a resource diff --git a/WickedEngine/wiScene_Components.cpp b/WickedEngine/wiScene_Components.cpp index 800c1199a..8d5727893 100644 --- a/WickedEngine/wiScene_Components.cpp +++ b/WickedEngine/wiScene_Components.cpp @@ -400,23 +400,43 @@ namespace wi::scene } return FILTER_TRANSPARENT; } - void MaterialComponent::CreateRenderData() + wi::resourcemanager::Flags MaterialComponent::GetTextureSlotResourceFlags(TEXTURESLOT slot) { + wi::resourcemanager::Flags flags = wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA; + if (!IsPreferUncompressedTexturesEnabled()) + { + flags |= wi::resourcemanager::Flags::IMPORT_BLOCK_COMPRESSED; + } + switch (slot) + { + case NORMALMAP: + case CLEARCOATNORMALMAP: + flags |= wi::resourcemanager::Flags::IMPORT_NORMALMAP; + break; + default: + break; + } + return flags; + } + void MaterialComponent::CreateRenderData(bool force_recreate) + { + if (force_recreate) + { + for (uint32_t slot = 0; slot < TEXTURESLOT_COUNT; ++slot) + { + auto& textureslot = textures[slot]; + if (textureslot.resource.IsValid()) + { + textureslot.resource.SetOutdated(); + } + } + } for (uint32_t slot = 0; slot < TEXTURESLOT_COUNT; ++slot) { auto& textureslot = textures[slot]; if (!textureslot.name.empty()) { - wi::resourcemanager::Flags flags = wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA; - switch (slot) - { - case NORMALMAP: - case CLEARCOATNORMALMAP: - flags |= wi::resourcemanager::Flags::IMPORT_NORMALMAP; - break; - default: - break; - } + wi::resourcemanager::Flags flags = GetTextureSlotResourceFlags(TEXTURESLOT(slot)); textureslot.resource = wi::resourcemanager::Load(textureslot.name, flags); } } diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index 1b5ede85f..5618dace0 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -120,6 +120,7 @@ namespace wi::scene DISABLE_RECEIVE_SHADOW = 1 << 10, DOUBLE_SIDED = 1 << 11, OUTLINE = 1 << 12, + PREFER_UNCOMPRESSED_TEXTURES = 1 << 13, }; uint32_t _flags = CAST_SHADOW; @@ -262,8 +263,9 @@ namespace wi::scene inline bool IsOcclusionEnabled_Primary() const { return _flags & OCCLUSION_PRIMARY; } inline bool IsOcclusionEnabled_Secondary() const { return _flags & OCCLUSION_SECONDARY; } inline bool IsCustomShader() const { return customShaderID >= 0; } - inline bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; } - inline bool IsOutlineEnabled() const { return _flags & OUTLINE; } + inline bool IsDoubleSided() const { return _flags & DOUBLE_SIDED; } + inline bool IsOutlineEnabled() const { return _flags & OUTLINE; } + inline bool IsPreferUncompressedTexturesEnabled() const { return _flags & PREFER_UNCOMPRESSED_TEXTURES; } inline void SetBaseColor(const XMFLOAT4& value) { SetDirty(); baseColor = value; } inline void SetSpecularColor(const XMFLOAT4& value) { SetDirty(); specularColor = value; } @@ -302,6 +304,7 @@ namespace wi::scene inline void DisableCustomShader() { customShaderID = -1; } inline void SetDoubleSided(bool value = true) { if (value) { _flags |= DOUBLE_SIDED; } else { _flags &= ~DOUBLE_SIDED; } } inline void SetOutlineEnabled(bool value = true) { if (value) { _flags |= OUTLINE; } else { _flags &= ~OUTLINE; } } + inline void SetPreferUncompressedTexturesEnabled(bool value = true) { if (value) { _flags |= PREFER_UNCOMPRESSED_TEXTURES; } else { _flags &= ~PREFER_UNCOMPRESSED_TEXTURES; } CreateRenderData(true); } // The MaterialComponent will be written to ShaderMaterial (a struct that is optimized for GPU use) void WriteShaderMaterial(ShaderMaterial* dest) const; @@ -313,8 +316,10 @@ namespace wi::scene // Returns the bitwise OR of all the wi::enums::FILTER flags applicable to this material uint32_t GetFilterMask() const; - // Create constant buffer and texture resources for GPU - void CreateRenderData(); + wi::resourcemanager::Flags GetTextureSlotResourceFlags(TEXTURESLOT slot); + + // Create texture resources for GPU + void CreateRenderData(bool force_recreate = false); void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 204156663..1272aa91d 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -2033,6 +2033,25 @@ namespace wi::scene ddgi.Serialize(archive); } + wi::jobsystem::Wait(seri.ctx); // This is needed before emitter material fixup that is below, because material CreateRenderDatas might be pending! + + // Fixup old emittedparticle distortion basecolor slot -> normalmap slot + if (archive.GetVersion() < 89) + { + for (size_t i = 0; i < emitters.GetCount(); ++i) + { + if (emitters[i].shaderType != EmittedParticleSystem::PARTICLESHADERTYPE::SOFT_DISTORTION) + continue; + Entity entity = emitters.GetEntity(i); + MaterialComponent* material = materials.GetComponent(entity); + if (material != nullptr) + { + material->textures[NORMALMAP] = std::move(material->textures[BASECOLORMAP]); + material->CreateRenderData(true); + } + } + } + wi::backlog::post("Scene serialize took " + std::to_string(timer.elapsed_seconds()) + " sec"); } diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index e7505dcc1..24c485a8b 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 213; + const int revision = 214; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision); diff --git a/third_party_software.txt b/third_party_software.txt index 1ea62f449..1b5dcd8ff 100644 --- a/third_party_software.txt +++ b/third_party_software.txt @@ -510,6 +510,33 @@ OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR SOFTWARE. +############################################################################################################################### + +Compressonator: https://github.com/GPUOpen-Tools/compressonator + +//=============================================================================== +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//=============================================================================== + ############################################################################################################################### pugixml: https://github.com/zeux/pugixml