Commit 8adc5469 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Generate mipmap in compute

An initial implementation based on AMD's FFX SPD (Single Pass Downsampler). Apart from requiring STORAGE_IMAGE support for the image format, the following limitations are in place due to FFX SPD: - Image must be 2D or 2D array (including cube maps) - Image must be single-sampled The following _can_ be supported, but not yet implemented: - sRGB formats - Integer formats - depth/stencil formats Bug: angleproject:4551 Change-Id: Ibc4d5cea701cca31e55e3d651540872bbd3b473f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2278713Reviewed-by: 's avatarMohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent bef908ee
[ [
"src/common/third_party/xxhash", "src/common/third_party/xxhash",
"src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd",
"src/third_party/compiler", "src/third_party/compiler",
"third_party/vulkan_memory_allocator" "third_party/vulkan_memory_allocator"
] ]
...@@ -344,6 +344,22 @@ struct FeaturesVk : FeatureSetBase ...@@ -344,6 +344,22 @@ struct FeaturesVk : FeatureSetBase
"enable_command_processing_thread", FeatureCategory::VulkanFeatures, "enable_command_processing_thread", FeatureCategory::VulkanFeatures,
"Enable parallel processing and submission of Vulkan commands in worker thread", &members, "Enable parallel processing and submission of Vulkan commands in worker thread", &members,
"http://anglebug.com/4324"}; "http://anglebug.com/4324"};
// Whether the VkDevice supports the VK_KHR_shader_float16_int8 extension and has the
// shaderFloat16 feature.
Feature supportsShaderFloat16 = {"supports_shader_float16", FeatureCategory::VulkanFeatures,
"VkDevice supports the VK_KHR_shader_float16_int8 extension "
"and has the shaderFloat16 feature",
&members, "http://anglebug.com/4551"};
// Some devices don't meet the limits required to perform mipmap generation using the built-in
// compute shader. On some other devices, VK_IMAGE_USAGE_STORAGE_BIT is detrimental to
// performance, making this solution impractical.
Feature allowGenerateMipmapWithCompute = {
"allow_generate_mipmap_with_compute", FeatureCategory::VulkanFeatures,
"Use the compute path to generate mipmaps on devices that meet the minimum requirements, "
"and the performance is better.",
&members, "http://anglebug.com/4551"};
}; };
inline FeaturesVk::FeaturesVk() = default; inline FeaturesVk::FeaturesVk() = default;
......
...@@ -91,6 +91,26 @@ ...@@ -91,6 +91,26 @@
"c9e0fad17170e97662b0fa0d37919ea3", "c9e0fad17170e97662b0fa0d37919ea3",
"src/libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc": "src/libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc":
"3a4ab796f02d3f1c306c92f7da2c68ee", "3a4ab796f02d3f1c306c92f7da2c68ee",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000000.inc":
"f06dfe4a68246020bc3d2c1132e6bd11",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000001.inc":
"86b35ef5a4f4058bc59c57157bc2a9e8",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000002.inc":
"bbf41ecbd4b7065cdc02dd91bed2b1c1",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000003.inc":
"197553fd8abfce67e5aa5abaf4fac069",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000004.inc":
"6173c8b287c379473195c7f2c184d682",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000005.inc":
"77da7b66604d76872d56d4a7e87d2e92",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000006.inc":
"e151f06ebf603206191017aa902db11c",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000007.inc":
"10565b3877038c05d6dcfeaf31d79a78",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000008.inc":
"4503b38c206416b27b2c5efee1229d97",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000009.inc":
"b6eae7691b963374795cacf6659f9a2a",
"src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000000.inc": "src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000000.inc":
"d5a8184fad9c4a315bc3146ad07d3991", "d5a8184fad9c4a315bc3146ad07d3991",
"src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000001.inc": "src/libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000001.inc":
...@@ -235,6 +255,10 @@ ...@@ -235,6 +255,10 @@
"e4a95aae7f216780946e7332d22aa74e", "e4a95aae7f216780946e7332d22aa74e",
"src/libANGLE/renderer/vulkan/shaders/src/FullScreenQuad.vert": "src/libANGLE/renderer/vulkan/shaders/src/FullScreenQuad.vert":
"805ec8b2f87d4bd4242dc5b1c58ba3b4", "805ec8b2f87d4bd4242dc5b1c58ba3b4",
"src/libANGLE/renderer/vulkan/shaders/src/GenerateMipmap.comp":
"46ac9e6bbb1b6f2af92dcc85ba52faff",
"src/libANGLE/renderer/vulkan/shaders/src/GenerateMipmap.comp.json":
"99543d519f0afe66713498e5d7873871",
"src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag": "src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag":
"8889ae8014a657a0efd5607954126945", "8889ae8014a657a0efd5607954126945",
"src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag.json": "src/libANGLE/renderer/vulkan/shaders/src/ImageClear.frag.json":
...@@ -252,9 +276,9 @@ ...@@ -252,9 +276,9 @@
"src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp.json": "src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp.json":
"af79e5153c99cdb1e6b551b11bbf7f6b", "af79e5153c99cdb1e6b551b11bbf7f6b",
"src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.cpp": "src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.cpp":
"6cd9f03e2a574c99570651e033fb6269", "df9efda2dee207bea8e346f2c9fa824f",
"src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h": "src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h":
"4dc3b23be0176a3e8e092dbb20522a9d", "96c6f4b7a06a3b21f3b0ceea661014ef",
"tools/glslang/glslang_validator.exe.sha1": "tools/glslang/glslang_validator.exe.sha1":
"17e862cc6f462fecbf50b24ed6544a27", "17e862cc6f462fecbf50b24ed6544a27",
"tools/glslang/glslang_validator.sha1": "tools/glslang/glslang_validator.sha1":
......
...@@ -766,7 +766,7 @@ void ContextVk::onDestroy(const gl::Context *context) ...@@ -766,7 +766,7 @@ void ContextVk::onDestroy(const gl::Context *context)
mResourceUseList.releaseResourceUses(); mResourceUseList.releaseResourceUses();
mUtils.destroy(device); mUtils.destroy(mRenderer);
mRenderPassCache.destroy(device); mRenderPassCache.destroy(device);
mSubmitFence.reset(device); mSubmitFence.reset(device);
......
...@@ -870,6 +870,10 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt ...@@ -870,6 +870,10 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt
mExternalMemoryHostProperties.sType = mExternalMemoryHostProperties.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT; VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT;
mShaderFloat16Int8Features = {};
mShaderFloat16Int8Features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
mExternalFenceProperties = {}; mExternalFenceProperties = {};
mExternalFenceProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES; mExternalFenceProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES;
...@@ -935,6 +939,12 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt ...@@ -935,6 +939,12 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt
vk::AddToPNextChain(&deviceFeatures, &mSamplerYcbcrConversionFeatures); vk::AddToPNextChain(&deviceFeatures, &mSamplerYcbcrConversionFeatures);
} }
// Query float16/int8 features
if (ExtensionFound(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, deviceExtensionNames))
{
vk::AddToPNextChain(&deviceFeatures, &mShaderFloat16Int8Features);
}
// Query subgroup properties // Query subgroup properties
vk::AddToPNextChain(&deviceProperties, &mSubgroupProperties); vk::AddToPNextChain(&deviceProperties, &mSubgroupProperties);
...@@ -979,6 +989,7 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt ...@@ -979,6 +989,7 @@ void RendererVk::queryDeviceExtensionFeatures(const ExtensionNameList &deviceExt
mTransformFeedbackFeatures.pNext = nullptr; mTransformFeedbackFeatures.pNext = nullptr;
mIndexTypeUint8Features.pNext = nullptr; mIndexTypeUint8Features.pNext = nullptr;
mSamplerYcbcrConversionFeatures.pNext = nullptr; mSamplerYcbcrConversionFeatures.pNext = nullptr;
mShaderFloat16Int8Features.pNext = nullptr;
} }
angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex) angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex)
...@@ -1289,6 +1300,12 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF ...@@ -1289,6 +1300,12 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
vk::AddToPNextChain(&createInfo, &mSamplerYcbcrConversionFeatures); vk::AddToPNextChain(&createInfo, &mSamplerYcbcrConversionFeatures);
} }
if (getFeatures().supportsShaderFloat16.enabled)
{
enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
vk::AddToPNextChain(&createInfo, &mShaderFloat16Int8Features);
}
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.flags = 0; createInfo.flags = 0;
createInfo.queueCreateInfoCount = 1; createInfo.queueCreateInfoCount = 1;
...@@ -1752,6 +1769,26 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1752,6 +1769,26 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
ANGLE_FEATURE_CONDITION(&mFeatures, supportsYUVSamplerConversion, ANGLE_FEATURE_CONDITION(&mFeatures, supportsYUVSamplerConversion,
mSamplerYcbcrConversionFeatures.samplerYcbcrConversion != VK_FALSE); mSamplerYcbcrConversionFeatures.samplerYcbcrConversion != VK_FALSE);
ANGLE_FEATURE_CONDITION(&mFeatures, supportsShaderFloat16,
mShaderFloat16Int8Features.shaderFloat16 == VK_TRUE);
// The compute shader used to generate mipmaps uses a 256-wide workgroup. This path is only
// enabled on devices that meet this minimum requirement. Furthermore,
// VK_IMAGE_USAGE_STORAGE_BIT is detrimental to performance on many platforms, on which this
// path is not enabled. Platforms that are known to have better performance with this path are:
//
// - Nvidia
// - AMD
//
// Additionally, this path is disabled on buggy drivers:
//
// - AMD/Windows: Unfortunately the trybots use ancient AMD cards and drivers.
const uint32_t maxComputeWorkGroupInvocations =
mPhysicalDeviceProperties.limits.maxComputeWorkGroupInvocations;
ANGLE_FEATURE_CONDITION(
&mFeatures, allowGenerateMipmapWithCompute,
maxComputeWorkGroupInvocations >= 256 && (isNvidia || (isAMD && !IsWindows())));
angle::PlatformMethods *platform = ANGLEPlatformCurrent(); angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures); platform->overrideFeaturesVk(platform, &mFeatures);
......
...@@ -306,6 +306,7 @@ class RendererVk : angle::NonCopyable ...@@ -306,6 +306,7 @@ class RendererVk : angle::NonCopyable
VkPhysicalDeviceIndexTypeUint8FeaturesEXT mIndexTypeUint8Features; VkPhysicalDeviceIndexTypeUint8FeaturesEXT mIndexTypeUint8Features;
VkPhysicalDeviceSubgroupProperties mSubgroupProperties; VkPhysicalDeviceSubgroupProperties mSubgroupProperties;
VkPhysicalDeviceExternalMemoryHostPropertiesEXT mExternalMemoryHostProperties; VkPhysicalDeviceExternalMemoryHostPropertiesEXT mExternalMemoryHostProperties;
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR mShaderFloat16Int8Features;
VkExternalFenceProperties mExternalFenceProperties; VkExternalFenceProperties mExternalFenceProperties;
VkExternalSemaphoreProperties mExternalSemaphoreProperties; VkExternalSemaphoreProperties mExternalSemaphoreProperties;
VkPhysicalDeviceSamplerYcbcrConversionFeatures mSamplerYcbcrConversionFeatures; VkPhysicalDeviceSamplerYcbcrConversionFeatures mSamplerYcbcrConversionFeatures;
......
...@@ -278,6 +278,13 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -278,6 +278,13 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
const gl::Box &sourceArea, const gl::Box &sourceArea,
size_t offset); size_t offset);
// Called from syncState to prepare the image for mipmap generation.
void prepareForGenerateMipmap(ContextVk *contextVk);
// Generate mipmaps from level 0 into the rest of the mips. This requires the image to have
// STORAGE usage.
angle::Result generateMipmapsWithCompute(ContextVk *contextVk);
angle::Result generateMipmapsWithCPU(const gl::Context *context); angle::Result generateMipmapsWithCPU(const gl::Context *context);
angle::Result generateMipmapLevelsWithCPU(ContextVk *contextVk, angle::Result generateMipmapLevelsWithCPU(ContextVk *contextVk,
......
...@@ -25,6 +25,7 @@ namespace BlitResolveStencilNoExport_comp = vk::InternalShader::BlitResolveSte ...@@ -25,6 +25,7 @@ namespace BlitResolveStencilNoExport_comp = vk::InternalShader::BlitResolveSte
namespace OverlayCull_comp = vk::InternalShader::OverlayCull_comp; namespace OverlayCull_comp = vk::InternalShader::OverlayCull_comp;
namespace OverlayDraw_comp = vk::InternalShader::OverlayDraw_comp; namespace OverlayDraw_comp = vk::InternalShader::OverlayDraw_comp;
namespace ConvertIndexIndirectLineLoop_comp = vk::InternalShader::ConvertIndexIndirectLineLoop_comp; namespace ConvertIndexIndirectLineLoop_comp = vk::InternalShader::ConvertIndexIndirectLineLoop_comp;
namespace GenerateMipmap_comp = vk::InternalShader::GenerateMipmap_comp;
namespace namespace
{ {
...@@ -48,6 +49,8 @@ constexpr uint32_t kOverlayDrawTextWidgetsBinding = 1; ...@@ -48,6 +49,8 @@ constexpr uint32_t kOverlayDrawTextWidgetsBinding = 1;
constexpr uint32_t kOverlayDrawGraphWidgetsBinding = 2; constexpr uint32_t kOverlayDrawGraphWidgetsBinding = 2;
constexpr uint32_t kOverlayDrawCulledWidgetsBinding = 3; constexpr uint32_t kOverlayDrawCulledWidgetsBinding = 3;
constexpr uint32_t kOverlayDrawFontBinding = 4; constexpr uint32_t kOverlayDrawFontBinding = 4;
constexpr uint32_t kGenerateMipmapDestinationBinding = 0;
constexpr uint32_t kGenerateMipmapSourceBinding = 1;
uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters &params) uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters &params)
{ {
...@@ -235,6 +238,39 @@ uint32_t GetConvertIndexIndirectLineLoopFlag(uint32_t indicesBitsWidth) ...@@ -235,6 +238,39 @@ uint32_t GetConvertIndexIndirectLineLoopFlag(uint32_t indicesBitsWidth)
} }
} }
uint32_t GetGenerateMipmapFlags(ContextVk *contextVk, const vk::Format &format)
{
const angle::Format &actualFormat = format.actualImageFormat();
uint32_t flags = 0;
// Note: If bits-per-component is 8 or 16 and float16 is supported in the shader, use that for
// faster math.
const bool hasShaderFloat16 =
contextVk->getRenderer()->getFeatures().supportsShaderFloat16.enabled;
if (actualFormat.redBits <= 8)
{
flags = hasShaderFloat16 ? GenerateMipmap_comp::kIsRGBA8_UseHalf
: GenerateMipmap_comp::kIsRGBA8;
}
else if (actualFormat.redBits <= 16)
{
flags = hasShaderFloat16 ? GenerateMipmap_comp::kIsRGBA16_UseHalf
: GenerateMipmap_comp::kIsRGBA16;
}
else
{
flags = GenerateMipmap_comp::kIsRGBA32F;
}
flags |= UtilsVk::GetGenerateMipmapMaxLevels(contextVk) == UtilsVk::kGenerateMipmapMaxLevels
? GenerateMipmap_comp::kDestSize6
: GenerateMipmap_comp::kDestSize4;
return flags;
}
uint32_t GetFormatDefaultChannelMask(const vk::Format &format) uint32_t GetFormatDefaultChannelMask(const vk::Format &format)
{ {
uint32_t mask = 0; uint32_t mask = 0;
...@@ -274,16 +310,38 @@ void CalculateResolveOffset(const UtilsVk::BlitResolveParameters &params, int32_ ...@@ -274,16 +310,38 @@ void CalculateResolveOffset(const UtilsVk::BlitResolveParameters &params, int32_
} }
} // namespace } // namespace
const uint32_t UtilsVk::kGenerateMipmapMaxLevels;
UtilsVk::ConvertVertexShaderParams::ConvertVertexShaderParams() = default; UtilsVk::ConvertVertexShaderParams::ConvertVertexShaderParams() = default;
UtilsVk::ImageCopyShaderParams::ImageCopyShaderParams() = default; UtilsVk::ImageCopyShaderParams::ImageCopyShaderParams() = default;
uint32_t UtilsVk::GetGenerateMipmapMaxLevels(ContextVk *contextVk)
{
RendererVk *renderer = contextVk->getRenderer();
uint32_t maxPerStageDescriptorStorageImages =
renderer->getPhysicalDeviceProperties().limits.maxPerStageDescriptorStorageImages;
// Vulkan requires that there be support for at least 4 storage images per stage.
constexpr uint32_t kMinimumStorageImagesLimit = 4;
ASSERT(maxPerStageDescriptorStorageImages >= kMinimumStorageImagesLimit);
// If fewer than max-levels are supported, use 4 levels (which is the minimum required number
// of storage image bindings).
return maxPerStageDescriptorStorageImages < kGenerateMipmapMaxLevels
? kMinimumStorageImagesLimit
: kGenerateMipmapMaxLevels;
}
UtilsVk::UtilsVk() = default; UtilsVk::UtilsVk() = default;
UtilsVk::~UtilsVk() = default; UtilsVk::~UtilsVk() = default;
void UtilsVk::destroy(VkDevice device) void UtilsVk::destroy(RendererVk *renderer)
{ {
VkDevice device = renderer->getDevice();
for (Function f : angle::AllEnums<Function>()) for (Function f : angle::AllEnums<Function>())
{ {
for (auto &descriptorSetLayout : mDescriptorSetLayouts[f]) for (auto &descriptorSetLayout : mDescriptorSetLayouts[f])
...@@ -335,6 +393,10 @@ void UtilsVk::destroy(VkDevice device) ...@@ -335,6 +393,10 @@ void UtilsVk::destroy(VkDevice device)
{ {
program.destroy(device); program.destroy(device);
} }
for (vk::ShaderProgramHelper &program : mGenerateMipmapPrograms)
{
program.destroy(device);
}
mPointSampler.destroy(device); mPointSampler.destroy(device);
mLinearSampler.destroy(device); mLinearSampler.destroy(device);
...@@ -358,7 +420,7 @@ angle::Result UtilsVk::ensureResourcesInitialized(ContextVk *contextVk, ...@@ -358,7 +420,7 @@ angle::Result UtilsVk::ensureResourcesInitialized(ContextVk *contextVk,
{ {
descriptorSetDesc.update(currentBinding, setSizes[i].type, setSizes[i].descriptorCount, descriptorSetDesc.update(currentBinding, setSizes[i].type, setSizes[i].descriptorCount,
descStages, nullptr); descStages, nullptr);
currentBinding += setSizes[i].descriptorCount; ++currentBinding;
} }
ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, descriptorSetDesc, ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, descriptorSetDesc,
...@@ -573,6 +635,22 @@ angle::Result UtilsVk::ensureOverlayDrawResourcesInitialized(ContextVk *contextV ...@@ -573,6 +635,22 @@ angle::Result UtilsVk::ensureOverlayDrawResourcesInitialized(ContextVk *contextV
return ensureSamplersInitialized(contextVk); return ensureSamplersInitialized(contextVk);
} }
angle::Result UtilsVk::ensureGenerateMipmapResourcesInitialized(ContextVk *contextVk)
{
if (mPipelineLayouts[Function::GenerateMipmap].valid())
{
return angle::Result::Continue;
}
VkDescriptorPoolSize setSizes[2] = {
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, GetGenerateMipmapMaxLevels(contextVk)},
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1},
};
return ensureResourcesInitialized(contextVk, Function::GenerateMipmap, setSizes,
ArraySize(setSizes), sizeof(GenerateMipmapShaderParams));
}
angle::Result UtilsVk::ensureSamplersInitialized(ContextVk *contextVk) angle::Result UtilsVk::ensureSamplersInitialized(ContextVk *contextVk)
{ {
VkSamplerCreateInfo samplerInfo = {}; VkSamplerCreateInfo samplerInfo = {};
...@@ -1779,6 +1857,84 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk, ...@@ -1779,6 +1857,84 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result UtilsVk::generateMipmap(ContextVk *contextVk,
vk::ImageHelper *src,
const vk::ImageView *srcLevelZeroView,
vk::ImageHelper *dest,
const GenerateMipmapDestLevelViews &destLevelViews,
const vk::Sampler &sampler,
const GenerateMipmapParameters &params)
{
ANGLE_TRY(ensureGenerateMipmapResourcesInitialized(contextVk));
const gl::Extents &srcExtents = src->getLevelExtents(params.srcLevel);
ASSERT(srcExtents.depth == 1);
// Each workgroup processes a 64x64 tile of the image.
constexpr uint32_t kPixelWorkgroupRatio = 64;
const uint32_t workGroupX = UnsignedCeilDivide(srcExtents.width, kPixelWorkgroupRatio);
const uint32_t workGroupY = UnsignedCeilDivide(srcExtents.height, kPixelWorkgroupRatio);
GenerateMipmapShaderParams shaderParams;
shaderParams.levelCount = params.destLevelCount;
shaderParams.numWorkGroups = workGroupX * workGroupY;
shaderParams.invSrcExtent[0] = 1.0f / srcExtents.width;
shaderParams.invSrcExtent[1] = 1.0f / srcExtents.height;
uint32_t flags = GetGenerateMipmapFlags(contextVk, src->getFormat());
VkDescriptorSet descriptorSet;
vk::RefCountedDescriptorPoolBinding descriptorPoolBinding;
ANGLE_TRY(allocateDescriptorSet(contextVk, Function::GenerateMipmap, &descriptorPoolBinding,
&descriptorSet));
VkDescriptorImageInfo destImageInfos[kGenerateMipmapMaxLevels] = {};
for (uint32_t level = 0; level < kGenerateMipmapMaxLevels; ++level)
{
destImageInfos[level].imageView = destLevelViews[level]->getHandle();
destImageInfos[level].imageLayout = dest->getCurrentLayout();
}
VkDescriptorImageInfo srcImageInfo = {};
srcImageInfo.imageView = srcLevelZeroView->getHandle();
srcImageInfo.imageLayout = src->getCurrentLayout();
srcImageInfo.sampler = sampler.getHandle();
VkWriteDescriptorSet writeInfos[2] = {};
writeInfos[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfos[0].dstSet = descriptorSet;
writeInfos[0].dstBinding = kGenerateMipmapDestinationBinding;
writeInfos[0].descriptorCount = GetGenerateMipmapMaxLevels(contextVk);
writeInfos[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writeInfos[0].pImageInfo = destImageInfos;
writeInfos[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfos[1].dstSet = descriptorSet;
writeInfos[1].dstBinding = kGenerateMipmapSourceBinding;
writeInfos[1].descriptorCount = 1;
writeInfos[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeInfos[1].pImageInfo = &srcImageInfo;
vkUpdateDescriptorSets(contextVk->getDevice(), 2, writeInfos, 0, nullptr);
vk::RefCounted<vk::ShaderAndSerial> *shader = nullptr;
ANGLE_TRY(contextVk->getShaderLibrary().getGenerateMipmap_comp(contextVk, flags, &shader));
// Note: onImageRead/onImageWrite is expected to be called by the caller. This avoids inserting
// barriers between calls for each layer of the image.
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
ANGLE_TRY(setupProgram(contextVk, Function::GenerateMipmap, shader, nullptr,
&mGenerateMipmapPrograms[flags], nullptr, descriptorSet, &shaderParams,
sizeof(shaderParams), commandBuffer));
commandBuffer->dispatch(workGroupX, workGroupY, 1);
descriptorPoolBinding.reset();
return angle::Result::Continue;
}
angle::Result UtilsVk::cullOverlayWidgets(ContextVk *contextVk, angle::Result UtilsVk::cullOverlayWidgets(ContextVk *contextVk,
vk::BufferHelper *enabledWidgetsBuffer, vk::BufferHelper *enabledWidgetsBuffer,
vk::ImageHelper *dest, vk::ImageHelper *dest,
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
// on color images. // on color images.
// - Depth/Stencil blit/resolve: Used by FramebufferVk::blit() to implement blit or multisample // - Depth/Stencil blit/resolve: Used by FramebufferVk::blit() to implement blit or multisample
// resolve on depth/stencil images. // resolve on depth/stencil images.
// - Generate mipmap: Used by TextureVk::generateMipmapsWithCompute().
// - Overlay Cull/Draw: Used by OverlayVk to efficiently draw a UI for debugging. // - Overlay Cull/Draw: Used by OverlayVk to efficiently draw a UI for debugging.
// - Mipmap generation: Not yet implemented // - Mipmap generation: Not yet implemented
// //
...@@ -37,7 +38,7 @@ class UtilsVk : angle::NonCopyable ...@@ -37,7 +38,7 @@ class UtilsVk : angle::NonCopyable
UtilsVk(); UtilsVk();
~UtilsVk(); ~UtilsVk();
void destroy(VkDevice device); void destroy(RendererVk *renderer);
struct ClearParameters struct ClearParameters
{ {
...@@ -157,6 +158,16 @@ class UtilsVk : angle::NonCopyable ...@@ -157,6 +158,16 @@ class UtilsVk : angle::NonCopyable
uint32_t subgroupSize[2]; uint32_t subgroupSize[2];
}; };
struct GenerateMipmapParameters
{
uint32_t srcLevel;
uint32_t destLevelCount;
};
// Based on the maximum number of levels in GenerateMipmap.comp.
static constexpr uint32_t kGenerateMipmapMaxLevels = 6;
static uint32_t GetGenerateMipmapMaxLevels(ContextVk *contextVk);
angle::Result convertIndexBuffer(ContextVk *contextVk, angle::Result convertIndexBuffer(ContextVk *contextVk,
vk::BufferHelper *dest, vk::BufferHelper *dest,
vk::BufferHelper *src, vk::BufferHelper *src,
...@@ -217,6 +228,16 @@ class UtilsVk : angle::NonCopyable ...@@ -217,6 +228,16 @@ class UtilsVk : angle::NonCopyable
const vk::ImageView *srcView, const vk::ImageView *srcView,
const CopyImageParameters &params); const CopyImageParameters &params);
using GenerateMipmapDestLevelViews =
std::array<const vk::ImageView *, kGenerateMipmapMaxLevels>;
angle::Result generateMipmap(ContextVk *contextVk,
vk::ImageHelper *src,
const vk::ImageView *srcLevelZeroView,
vk::ImageHelper *dest,
const GenerateMipmapDestLevelViews &destLevelViews,
const vk::Sampler &sampler,
const GenerateMipmapParameters &params);
// Overlay utilities. // Overlay utilities.
angle::Result cullOverlayWidgets(ContextVk *contextVk, angle::Result cullOverlayWidgets(ContextVk *contextVk,
vk::BufferHelper *enabledWidgetsBuffer, vk::BufferHelper *enabledWidgetsBuffer,
...@@ -360,6 +381,14 @@ class UtilsVk : angle::NonCopyable ...@@ -360,6 +381,14 @@ class UtilsVk : angle::NonCopyable
uint32_t outputSize[2] = {}; uint32_t outputSize[2] = {};
}; };
struct GenerateMipmapShaderParams
{
// Structure matching PushConstants in GenerateMipmap.comp
uint32_t levelCount = 0;
uint32_t numWorkGroups = 0;
float invSrcExtent[2] = {};
};
ANGLE_DISABLE_STRUCT_PADDING_WARNINGS ANGLE_DISABLE_STRUCT_PADDING_WARNINGS
// Functions implemented by the class: // Functions implemented by the class:
...@@ -380,9 +409,10 @@ class UtilsVk : angle::NonCopyable ...@@ -380,9 +409,10 @@ class UtilsVk : angle::NonCopyable
ConvertIndexIndirectBuffer = 8, ConvertIndexIndirectBuffer = 8,
ConvertIndexIndirectLineLoopBuffer = 9, ConvertIndexIndirectLineLoopBuffer = 9,
ConvertIndirectLineLoopBuffer = 10, ConvertIndirectLineLoopBuffer = 10,
GenerateMipmap = 11,
InvalidEnum = 11, InvalidEnum = 12,
EnumCount = 11, EnumCount = 12,
}; };
// Common function that creates the pipeline for the specified function, binds it and prepares // Common function that creates the pipeline for the specified function, binds it and prepares
...@@ -425,6 +455,7 @@ class UtilsVk : angle::NonCopyable ...@@ -425,6 +455,7 @@ class UtilsVk : angle::NonCopyable
angle::Result ensureBlitResolveStencilNoExportResourcesInitialized(ContextVk *contextVk); angle::Result ensureBlitResolveStencilNoExportResourcesInitialized(ContextVk *contextVk);
angle::Result ensureOverlayCullResourcesInitialized(ContextVk *contextVk); angle::Result ensureOverlayCullResourcesInitialized(ContextVk *contextVk);
angle::Result ensureOverlayDrawResourcesInitialized(ContextVk *contextVk); angle::Result ensureOverlayDrawResourcesInitialized(ContextVk *contextVk);
angle::Result ensureGenerateMipmapResourcesInitialized(ContextVk *contextVk);
angle::Result ensureSamplersInitialized(ContextVk *context); angle::Result ensureSamplersInitialized(ContextVk *context);
...@@ -469,6 +500,8 @@ class UtilsVk : angle::NonCopyable ...@@ -469,6 +500,8 @@ class UtilsVk : angle::NonCopyable
[vk::InternalShader::BlitResolveStencilNoExport_comp::kArrayLen]; [vk::InternalShader::BlitResolveStencilNoExport_comp::kArrayLen];
vk::ShaderProgramHelper mOverlayCullPrograms[vk::InternalShader::OverlayCull_comp::kArrayLen]; vk::ShaderProgramHelper mOverlayCullPrograms[vk::InternalShader::OverlayCull_comp::kArrayLen];
vk::ShaderProgramHelper mOverlayDrawPrograms[vk::InternalShader::OverlayDraw_comp::kArrayLen]; vk::ShaderProgramHelper mOverlayDrawPrograms[vk::InternalShader::OverlayDraw_comp::kArrayLen];
vk::ShaderProgramHelper
mGenerateMipmapPrograms[vk::InternalShader::GenerateMipmap_comp::kArrayLen];
vk::Sampler mPointSampler; vk::Sampler mPointSampler;
vk::Sampler mLinearSampler; vk::Sampler mLinearSampler;
......
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// GenerateMipmap.comp: Generate mipmap of texture in a single pass. Uses AMD's FFX SPD located in
// third_parth/ffx_spd/.
//
// Note that due to bugs in that code, we only support downsampling 6 levels at a time (instead of
// the 12 mips supported by FFX SPD). The issue is that FFX SPD tries to `imageLoad` from `dst[5]`
// with coordinates that can potentially be outside the level extents. This results in transparent
// black reads. A possible solution is to clamp the coodinates in `SpdLoad`. However, we opted to
// supporting only 6 levels at a time because:
//
// - On most Android vendors, which is our primary optimization target,
// maxPerStageDescriptorStorageImages is commonly 4, which means we cannot generate mipmaps for
// even 6 levels at a time anyway.
// - By removing support for >6 mips, we can remove the atomic counter logic required by FFX SPD to
// single out an invocation which will be downsampling the rest of the 6 mips. This makes the
// generation of the first 6 mips faster.
#version 450 core
#extension GL_GOOGLE_include_directive : require
#extension GL_EXT_samplerless_texture_functions : require
#if IsRGBA8 || IsRGBA8_UseHalf
#define DST_FORMAT rgba8
#elif IsRGBA16 || IsRGBA16_UseHalf
#define DST_FORMAT rgba16
#elif IsRGBA32F
#define DST_FORMAT rgba32f
#else
#error "Not all formats are accounted for"
#endif
#if DestSize4
#define DST_COUNT 4
#elif DestSize6
#define DST_COUNT 6
#else
#error "Not all destination sizes are accounted for"
#endif
// TODO: Support sRGB
// TODO: Support non-float formats
// TODO: Support subgroup mode
layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0, DST_FORMAT) uniform coherent image2D dst[DST_COUNT];
layout(set = 0, binding = 1) uniform sampler2D src;
layout(push_constant) uniform PushConstants {
// Number of levels to generate mipmaps for.
uint levelCount;
// Inverse extents of src image for uv calculation.
vec2 invSrcExtent;
} params;
#define A_GPU
#define A_GLSL
// For 8- and 16-bit-per-channel images, use half instead of float if supported.
#if IsRGBA8_UseHalf || IsRGBA16_UseHalf
#define A_HALF
#endif
#include "third_party/ffx_spd/ffx_a.h"
// Shared memory
#ifdef A_HALF
shared AH4 spd_intermediate[16][16];
#else
shared AF4 spd_intermediate[16][16];
#endif
shared AU1 spd_counter;
#define SPD_NO_WAVE_OPERATIONS
// Use a linear sampler to sample from mip 0 instead of multiple loads and manual averaging.
#define SPD_LINEAR_SAMPLER
// Utility functions used by ffx_spd.h
#ifdef A_HALF
#define SPD_PACKED_ONLY
// Load from source image
AH4 SpdLoadSourceImageH(ASU2 p)
{
AF2 textureCoord = p * params.invSrcExtent + params.invSrcExtent;
return AH4(texture(src, textureCoord));
}
// SpdLoadH() takes a 32-bit signed integer 2D coordinate and loads color.
// Loads the 5th mip level, each value is computed by a different thread group
// last thread group will access all its elements and compute the subsequent mips.
//
// Unused as we don't support more than 6 levels.
AH4 SpdLoadH(ASU2 p)
{
return AH4(0);
}
// Define the store function
void SpdStoreH(ASU2 p, AH4 value, AU1 mip)
{
imageStore(dst[mip], p, AF4(value));
}
// Define the lds load and store functions
AH4 SpdLoadIntermediateH(AU1 x, AU1 y)
{
return spd_intermediate[x][y];
}
void SpdStoreIntermediateH(AU1 x, AU1 y, AH4 value)
{
spd_intermediate[x][y] = value;
}
// Define your reduction function: takes as input the four 2x2 values and returns 1 output value
AH4 SpdReduce4H(AH4 v0, AH4 v1, AH4 v2, AH4 v3)
{
return (v0 + v1 + v2 + v3) * AH1(0.25);
}
#else // A_HALF
// Load from source image
AF4 SpdLoadSourceImage(ASU2 p)
{
AF2 textureCoord = p * params.invSrcExtent + params.invSrcExtent;
return texture(src, textureCoord);
}
// SpdLoad() takes a 32-bit signed integer 2D coordinate and loads color.
// Loads the 5th mip level, each value is computed by a different thread group
// last thread group will access all its elements and compute the subsequent mips
//
// Unused as we don't support more than 6 levels.
AF4 SpdLoad(ASU2 p)
{
return AF4(0);
}
// Define the store function
void SpdStore(ASU2 p, AF4 value, AU1 mip)
{
imageStore(dst[mip], p, value);
}
// Define the LDS load and store functions
AF4 SpdLoadIntermediate(AU1 x, AU1 y)
{
return spd_intermediate[x][y];
}
void SpdStoreIntermediate(AU1 x, AU1 y, AF4 value)
{
spd_intermediate[x][y] = value;
}
// Define your reduction function: takes as input the four 2x2 values and returns 1 output value
AF4 SpdReduce4(AF4 v0, AF4 v1, AF4 v2, AF4 v3)
{
return (v0 + v1 + v2 + v3) * 0.25;
}
#endif // A_HALF
// Define the atomic counter increase function. We don't support more than 6 mips, so these are
// unused. Returned value is arbitrary as SpdDownsample will early out before looking at it.
#define SpdIncreaseAtomicCounter()
#define SpdGetAtomicCounter() 0
#include "third_party/ffx_spd/ffx_spd.h"
void main()
{
#ifdef A_HALF
SpdDownsampleH(gl_WorkGroupID.xy, gl_LocalInvocationIndex, params.levelCount, 0);
#else
SpdDownsample(gl_WorkGroupID.xy, gl_LocalInvocationIndex, params.levelCount, 0);
#endif
}
{
"Description": [
"Copyright 2020 The ANGLE Project Authors. All rights reserved.",
"Use of this source code is governed by a BSD-style license that can be",
"found in the LICENSE file.",
"",
"GenerateMipmap.comp.json: Build parameters for GenerateMipmap.comp."
],
"MaxSupportedDest": [
"DestSize4",
"DestSize6"
],
"Format": [
"IsRGBA8",
"IsRGBA8_UseHalf",
"IsRGBA16",
"IsRGBA16_UseHalf",
"IsRGBA32F"
]
}
Copyright (c) 2020 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.
\ No newline at end of file
Name: FideltyFX Single Pass Downsampler
Short Name: ffx-spd
URL: https://github.com/GPUOpen-Effects/FidelityFX-SPD
Version: 1.0
Date: 31 May 2019
Revision: 0e82a6f4f491aa18adde62edc3f5c4ed20d92627
License: MIT
License File: LICENSE
Security Critical: No
Description:
A single-pass downsampler used to generate texture mipmaps.
Local Modifications:
None
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3332,8 +3332,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint ...@@ -3332,8 +3332,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
ANGLE_TRY(contextVk->onImageWrite(VK_IMAGE_ASPECT_COLOR_BIT, ImageLayout::TransferDst, this)); ANGLE_TRY(contextVk->onImageWrite(VK_IMAGE_ASPECT_COLOR_BIT, ImageLayout::TransferDst, this));
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer)); ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
// We are able to use blitImage since the image format we are using supports it. This // We are able to use blitImage since the image format we are using supports it.
// is a faster way we can generate the mips.
int32_t mipWidth = mExtents.width; int32_t mipWidth = mExtents.width;
int32_t mipHeight = mExtents.height; int32_t mipHeight = mExtents.height;
int32_t mipDepth = mExtents.depth; int32_t mipDepth = mExtents.depth;
...@@ -3350,11 +3349,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint ...@@ -3350,11 +3349,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
barrier.subresourceRange.layerCount = mLayerCount; barrier.subresourceRange.layerCount = mLayerCount;
barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.levelCount = 1;
const bool formatSupportsLinearFiltering = contextVk->getRenderer()->hasImageFormatFeatureBits( const VkFilter filter = gl_vk::GetFilter(CalculateGenerateMipmapFilter(contextVk, getFormat()));
getFormat().vkImageFormat, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);
const bool hintFastest = contextVk->getState().getGenerateMipmapHint() == GL_FASTEST;
const VkFilter filter =
formatSupportsLinearFiltering && !hintFastest ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
for (uint32_t mipLevel = 1; mipLevel <= maxLevel; mipLevel++) for (uint32_t mipLevel = 1; mipLevel <= maxLevel; mipLevel++)
{ {
......
...@@ -1184,6 +1184,7 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1184,6 +1184,7 @@ class ImageHelper final : public Resource, public angle::Subject
const VkImageSubresourceLayers &dstSubresources, const VkImageSubresourceLayers &dstSubresources,
CommandBuffer *commandBuffer); CommandBuffer *commandBuffer);
// Generate mipmap from level 0 into the rest of the levels with blit.
angle::Result generateMipmapsWithBlit(ContextVk *contextVk, GLuint maxLevel); angle::Result generateMipmapsWithBlit(ContextVk *contextVk, GLuint maxLevel);
// Resolve this image into a destination image. This image should be in the TransferSrc layout. // Resolve this image into a destination image. This image should be in the TransferSrc layout.
......
...@@ -64,6 +64,16 @@ namespace ...@@ -64,6 +64,16 @@ namespace
#include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000006.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000006.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc" #include "libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000000.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000001.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000002.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000003.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000004.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000005.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000006.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000007.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000008.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000009.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000000.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000000.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000001.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000001.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000002.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ImageClear.frag.00000002.inc"
...@@ -193,6 +203,18 @@ constexpr CompressedShaderBlob kConvertVertex_comp_shaders[] = { ...@@ -193,6 +203,18 @@ constexpr CompressedShaderBlob kConvertVertex_comp_shaders[] = {
constexpr CompressedShaderBlob kFullScreenQuad_vert_shaders[] = { constexpr CompressedShaderBlob kFullScreenQuad_vert_shaders[] = {
{kFullScreenQuad_vert_00000000, sizeof(kFullScreenQuad_vert_00000000)}, {kFullScreenQuad_vert_00000000, sizeof(kFullScreenQuad_vert_00000000)},
}; };
constexpr CompressedShaderBlob kGenerateMipmap_comp_shaders[] = {
{kGenerateMipmap_comp_00000000, sizeof(kGenerateMipmap_comp_00000000)},
{kGenerateMipmap_comp_00000001, sizeof(kGenerateMipmap_comp_00000001)},
{kGenerateMipmap_comp_00000002, sizeof(kGenerateMipmap_comp_00000002)},
{kGenerateMipmap_comp_00000003, sizeof(kGenerateMipmap_comp_00000003)},
{kGenerateMipmap_comp_00000004, sizeof(kGenerateMipmap_comp_00000004)},
{kGenerateMipmap_comp_00000005, sizeof(kGenerateMipmap_comp_00000005)},
{kGenerateMipmap_comp_00000006, sizeof(kGenerateMipmap_comp_00000006)},
{kGenerateMipmap_comp_00000007, sizeof(kGenerateMipmap_comp_00000007)},
{kGenerateMipmap_comp_00000008, sizeof(kGenerateMipmap_comp_00000008)},
{kGenerateMipmap_comp_00000009, sizeof(kGenerateMipmap_comp_00000009)},
};
constexpr CompressedShaderBlob kImageClear_frag_shaders[] = { constexpr CompressedShaderBlob kImageClear_frag_shaders[] = {
{kImageClear_frag_00000000, sizeof(kImageClear_frag_00000000)}, {kImageClear_frag_00000000, sizeof(kImageClear_frag_00000000)},
{kImageClear_frag_00000001, sizeof(kImageClear_frag_00000001)}, {kImageClear_frag_00000001, sizeof(kImageClear_frag_00000001)},
...@@ -350,6 +372,10 @@ void ShaderLibrary::destroy(VkDevice device) ...@@ -350,6 +372,10 @@ void ShaderLibrary::destroy(VkDevice device)
{ {
shader.get().destroy(device); shader.get().destroy(device);
} }
for (RefCounted<ShaderAndSerial> &shader : mGenerateMipmap_comp_shaders)
{
shader.get().destroy(device);
}
for (RefCounted<ShaderAndSerial> &shader : mImageClear_frag_shaders) for (RefCounted<ShaderAndSerial> &shader : mImageClear_frag_shaders)
{ {
shader.get().destroy(device); shader.get().destroy(device);
...@@ -430,6 +456,14 @@ angle::Result ShaderLibrary::getFullScreenQuad_vert(Context *context, ...@@ -430,6 +456,14 @@ angle::Result ShaderLibrary::getFullScreenQuad_vert(Context *context,
ArraySize(kFullScreenQuad_vert_shaders), shaderFlags, shaderOut); ArraySize(kFullScreenQuad_vert_shaders), shaderFlags, shaderOut);
} }
angle::Result ShaderLibrary::getGenerateMipmap_comp(Context *context,
uint32_t shaderFlags,
RefCounted<ShaderAndSerial> **shaderOut)
{
return GetShader(context, mGenerateMipmap_comp_shaders, kGenerateMipmap_comp_shaders,
ArraySize(kGenerateMipmap_comp_shaders), shaderFlags, shaderOut);
}
angle::Result ShaderLibrary::getImageClear_frag(Context *context, angle::Result ShaderLibrary::getImageClear_frag(Context *context,
uint32_t shaderFlags, uint32_t shaderFlags,
RefCounted<ShaderAndSerial> **shaderOut) RefCounted<ShaderAndSerial> **shaderOut)
......
...@@ -54,6 +54,16 @@ angle_vulkan_internal_shaders = [ ...@@ -54,6 +54,16 @@ angle_vulkan_internal_shaders = [
"shaders/gen/ConvertVertex.comp.00000006.inc", "shaders/gen/ConvertVertex.comp.00000006.inc",
"shaders/gen/ConvertVertex.comp.00000007.inc", "shaders/gen/ConvertVertex.comp.00000007.inc",
"shaders/gen/FullScreenQuad.vert.00000000.inc", "shaders/gen/FullScreenQuad.vert.00000000.inc",
"shaders/gen/GenerateMipmap.comp.00000000.inc",
"shaders/gen/GenerateMipmap.comp.00000001.inc",
"shaders/gen/GenerateMipmap.comp.00000002.inc",
"shaders/gen/GenerateMipmap.comp.00000003.inc",
"shaders/gen/GenerateMipmap.comp.00000004.inc",
"shaders/gen/GenerateMipmap.comp.00000005.inc",
"shaders/gen/GenerateMipmap.comp.00000006.inc",
"shaders/gen/GenerateMipmap.comp.00000007.inc",
"shaders/gen/GenerateMipmap.comp.00000008.inc",
"shaders/gen/GenerateMipmap.comp.00000009.inc",
"shaders/gen/ImageClear.frag.00000000.inc", "shaders/gen/ImageClear.frag.00000000.inc",
"shaders/gen/ImageClear.frag.00000001.inc", "shaders/gen/ImageClear.frag.00000001.inc",
"shaders/gen/ImageClear.frag.00000002.inc", "shaders/gen/ImageClear.frag.00000002.inc",
......
...@@ -95,6 +95,24 @@ namespace FullScreenQuad_vert ...@@ -95,6 +95,24 @@ namespace FullScreenQuad_vert
constexpr size_t kArrayLen = 0x00000001; constexpr size_t kArrayLen = 0x00000001;
} // namespace FullScreenQuad_vert } // namespace FullScreenQuad_vert
namespace GenerateMipmap_comp
{
enum MaxSupportedDest
{
kDestSize4 = 0x00000000,
kDestSize6 = 0x00000001,
};
enum Format
{
kIsRGBA8 = 0x00000000,
kIsRGBA8_UseHalf = 0x00000002,
kIsRGBA16 = 0x00000004,
kIsRGBA16_UseHalf = 0x00000006,
kIsRGBA32F = 0x00000008,
};
constexpr size_t kArrayLen = 0x0000000A;
} // namespace GenerateMipmap_comp
namespace ImageClear_frag namespace ImageClear_frag
{ {
enum AttachmentIndex enum AttachmentIndex
...@@ -197,6 +215,9 @@ class ShaderLibrary final : angle::NonCopyable ...@@ -197,6 +215,9 @@ class ShaderLibrary final : angle::NonCopyable
angle::Result getFullScreenQuad_vert(Context *context, angle::Result getFullScreenQuad_vert(Context *context,
uint32_t shaderFlags, uint32_t shaderFlags,
RefCounted<ShaderAndSerial> **shaderOut); RefCounted<ShaderAndSerial> **shaderOut);
angle::Result getGenerateMipmap_comp(Context *context,
uint32_t shaderFlags,
RefCounted<ShaderAndSerial> **shaderOut);
angle::Result getImageClear_frag(Context *context, angle::Result getImageClear_frag(Context *context,
uint32_t shaderFlags, uint32_t shaderFlags,
RefCounted<ShaderAndSerial> **shaderOut); RefCounted<ShaderAndSerial> **shaderOut);
...@@ -226,6 +247,8 @@ class ShaderLibrary final : angle::NonCopyable ...@@ -226,6 +247,8 @@ class ShaderLibrary final : angle::NonCopyable
RefCounted<ShaderAndSerial> RefCounted<ShaderAndSerial>
mFullScreenQuad_vert_shaders[InternalShader::FullScreenQuad_vert::kArrayLen]; mFullScreenQuad_vert_shaders[InternalShader::FullScreenQuad_vert::kArrayLen];
RefCounted<ShaderAndSerial> RefCounted<ShaderAndSerial>
mGenerateMipmap_comp_shaders[InternalShader::GenerateMipmap_comp::kArrayLen];
RefCounted<ShaderAndSerial>
mImageClear_frag_shaders[InternalShader::ImageClear_frag::kArrayLen]; mImageClear_frag_shaders[InternalShader::ImageClear_frag::kArrayLen];
RefCounted<ShaderAndSerial> mImageCopy_frag_shaders[InternalShader::ImageCopy_frag::kArrayLen]; RefCounted<ShaderAndSerial> mImageCopy_frag_shaders[InternalShader::ImageCopy_frag::kArrayLen];
RefCounted<ShaderAndSerial> RefCounted<ShaderAndSerial>
......
...@@ -902,6 +902,15 @@ void InitExternalSemaphoreCapabilitiesFunctions(VkInstance instance) ...@@ -902,6 +902,15 @@ void InitExternalSemaphoreCapabilitiesFunctions(VkInstance instance)
#endif // !defined(ANGLE_SHARED_LIBVULKAN) #endif // !defined(ANGLE_SHARED_LIBVULKAN)
GLenum CalculateGenerateMipmapFilter(ContextVk *contextVk, const vk::Format &format)
{
const bool formatSupportsLinearFiltering = contextVk->getRenderer()->hasImageFormatFeatureBits(
format.vkImageFormat, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT);
const bool hintFastest = contextVk->getState().getGenerateMipmapHint() == GL_FASTEST;
return formatSupportsLinearFiltering && !hintFastest ? GL_LINEAR : GL_NEAREST;
}
namespace gl_vk namespace gl_vk
{ {
......
...@@ -732,6 +732,8 @@ void InitExternalSemaphoreCapabilitiesFunctions(VkInstance instance); ...@@ -732,6 +732,8 @@ void InitExternalSemaphoreCapabilitiesFunctions(VkInstance instance);
#endif // !defined(ANGLE_SHARED_LIBVULKAN) #endif // !defined(ANGLE_SHARED_LIBVULKAN)
GLenum CalculateGenerateMipmapFilter(ContextVk *contextVk, const vk::Format &format);
namespace gl_vk namespace gl_vk
{ {
VkRect2D GetRect(const gl::Rectangle &source); VkRect2D GetRect(const gl::Rectangle &source);
......
...@@ -376,6 +376,36 @@ void main() ...@@ -376,6 +376,36 @@ void main()
glDeleteProgram(mCubeProgram); glDeleteProgram(mCubeProgram);
} }
void verifyAllMips(const uint32_t textureWidth,
const uint32_t textureHeight,
const GLColor &color)
{
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Texture2DLod(),
essl3_shaders::fs::Texture2DLod());
glUseProgram(program);
const GLint textureLoc = glGetUniformLocation(program, essl3_shaders::Texture2DUniform());
const GLint lodLoc = glGetUniformLocation(program, essl3_shaders::LodUniform());
ASSERT_NE(-1, textureLoc);
ASSERT_NE(-1, lodLoc);
glUniform1i(textureLoc, 0);
// Verify that every mip is correct.
const int w = getWindowWidth() - 1;
const int h = getWindowHeight() - 1;
for (uint32_t mip = 0; textureWidth >> mip >= 1 || textureHeight >> mip >= 1; ++mip)
{
glUniform1f(lodLoc, mip);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, color) << "Failed on mip " << mip;
EXPECT_PIXEL_COLOR_EQ(w, 0, color) << "Failed on mip " << mip;
EXPECT_PIXEL_COLOR_EQ(0, h, color) << "Failed on mip " << mip;
EXPECT_PIXEL_COLOR_EQ(w, h, color) << "Failed on mip " << mip;
}
}
GLuint mTexture; GLuint mTexture;
GLuint mArrayProgram; GLuint mArrayProgram;
...@@ -404,6 +434,7 @@ class MipmapTestES31 : public BaseMipmapTest ...@@ -404,6 +434,7 @@ class MipmapTestES31 : public BaseMipmapTest
setConfigAlphaBits(8); setConfigAlphaBits(8);
} }
}; };
// This test uses init data for the first three levels of the texture. It passes the level 0 data // This test uses init data for the first three levels of the texture. It passes the level 0 data
// in, then renders, then level 1, then renders, etc. This ensures that renderers using the zero LOD // in, then renders, then level 1, then renders, etc. This ensures that renderers using the zero LOD
// workaround (e.g. D3D11 FL9_3) correctly pass init data to the mipmapped texture, even if the the // workaround (e.g. D3D11 FL9_3) correctly pass init data to the mipmapped texture, even if the the
...@@ -518,6 +549,108 @@ TEST_P(MipmapTest, DISABLED_ThreeLevelsInitData) ...@@ -518,6 +549,108 @@ TEST_P(MipmapTest, DISABLED_ThreeLevelsInitData)
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
} }
// This test generates mipmaps for a 1x1 texture, which should be a no-op.
TEST_P(MipmapTestES3, GenerateMipmap1x1Texture)
{
constexpr uint32_t kTextureSize = 1;
const std::vector<GLColor> kInitialColor(kTextureSize * kTextureSize,
GLColor(35, 81, 184, 211));
// Create the texture.
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, kInitialColor.data());
// Then generate the mips.
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GL_NO_ERROR();
// Verify that every mip is correct.
verifyAllMips(kTextureSize, kTextureSize, kInitialColor[0]);
}
// This test generates mipmaps for a large texture and ensures all mips are generated.
TEST_P(MipmapTestES3, GenerateMipmapLargeTexture)
{
constexpr uint32_t kTextureSize = 4096;
const std::vector<GLColor> kInitialColor(kTextureSize * kTextureSize,
GLColor(35, 81, 184, 211));
// Create the texture.
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, kInitialColor.data());
// Then generate the mips.
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GL_NO_ERROR();
// Enable mipmaps.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
// Verify that every mip is correct.
verifyAllMips(kTextureSize, kTextureSize, kInitialColor[0]);
}
// This test generates mipmaps for a large npot texture and ensures all mips are generated.
TEST_P(MipmapTestES3, GenerateMipmapLargeNPOTTexture)
{
constexpr uint32_t kTextureWidth = 3840;
constexpr uint32_t kTextureHeight = 2160;
const std::vector<GLColor> kInitialColor(kTextureWidth * kTextureHeight,
GLColor(35, 81, 184, 211));
// Create the texture.
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, kInitialColor.data());
// Then generate the mips.
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GL_NO_ERROR();
// Enable mipmaps.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
// Verify that every mip is correct.
verifyAllMips(kTextureWidth, kTextureHeight, kInitialColor[0]);
}
// This test generates mipmaps for an elongated npot texture with the maximum number of mips and
// ensures all mips are generated.
TEST_P(MipmapTestES3, GenerateMipmapLongNPOTTexture)
{
// Imprecisions in the result. http://anglebug.com/4821
ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL());
GLint maxTextureWidth = 32767;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureWidth);
constexpr uint32_t kTextureHeight = 43;
const uint32_t kTextureWidth = maxTextureWidth - 1; // -1 to make the width NPOT
const std::vector<GLColor> kInitialColor(kTextureWidth * kTextureHeight,
GLColor(35, 81, 184, 211));
// Create the texture.
glBindTexture(GL_TEXTURE_2D, mTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, kInitialColor.data());
// Then generate the mips.
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GL_NO_ERROR();
// Enable mipmaps.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
// Verify that every mip is correct.
verifyAllMips(kTextureWidth, kTextureHeight, kInitialColor[0]);
}
// This test generates (and uses) mipmaps on a texture using init data. D3D11 will use a // This test generates (and uses) mipmaps on a texture using init data. D3D11 will use a
// non-renderable TextureStorage for this. The test then disables mips, renders to level zero of the // non-renderable TextureStorage for this. The test then disables mips, renders to level zero of the
// texture, and reenables mips before using the texture again. To do this, D3D11 has to convert the // texture, and reenables mips before using the texture again. To do this, D3D11 has to convert the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment