blob: b510a738c064447864468cf08b2f618a3e7eb6b0 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "GraphicsDetectorVkPrecisionQualifiersOnYuvSamplers.h"
#include <vector>
#include "Image.h"
#include "Vulkan.h"
namespace gfxstream {
namespace {
// kBlitTextureVert
#include "shaders/blit_texture.vert.inl"
// kBlitTextureFrag
#include "shaders/blit_texture.frag.inl"
// kBlitTextureLowpFrag
#include "shaders/blit_texture_lowp.frag.inl"
// kBlitTextureMediumpFrag
#include "shaders/blit_texture_mediump.frag.inl"
// kBlitTextureHighpFrag
#include "shaders/blit_texture_highp.frag.inl"
gfxstream::expected<bool, vkhpp::Result>
CanHandlePrecisionQualifierWithYuvSampler(
const std::vector<uint8_t>& blitVertShaderSpirv,
const std::vector<uint8_t>& blitFragShaderSpirv) {
auto vk = VK_EXPECT(Vk::Load(
/*instance_extensions=*/{},
/*instance_layers=*/{},
/*device_extensions=*/
{
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
}));
uint32_t textureWidth = 32;
uint32_t textureHeight = 32;
RGBAImage textureDataRgba = FillWithColor(textureWidth,
textureHeight,
/*red=*/0xFF,
/*green=*/0x00,
/*blue=*/0x00,
/*alpha=*/0xFF);
YUV420Image textureDataYuv = ConvertRGBA8888ToYUV420(textureDataRgba);
#if 0
// Debugging can be easier with a larger image with more details.
textureDataYuv = GFXSTREAM_EXPECT(LoadYUV420FromBitmapFile("custom.bmp"));
#endif
Vk::YuvImageWithMemory sampledImage = VK_EXPECT(vk.CreateYuvImage(
textureWidth,
textureHeight,
vkhpp::ImageUsageFlagBits::eSampled |
vkhpp::ImageUsageFlagBits::eTransferDst |
vkhpp::ImageUsageFlagBits::eTransferSrc,
vkhpp::MemoryPropertyFlagBits::eDeviceLocal,
vkhpp::ImageLayout::eTransferDstOptimal));
VK_EXPECT_RESULT(vk.LoadYuvImage(sampledImage.image,
textureWidth,
textureHeight,
textureDataYuv.y,
textureDataYuv.u,
textureDataYuv.v,
/*currentLayout=*/vkhpp::ImageLayout::eTransferDstOptimal,
/*returnedLayout=*/vkhpp::ImageLayout::eShaderReadOnlyOptimal));
Vk::FramebufferWithAttachments framebuffer =
VK_EXPECT(vk.CreateFramebuffer(textureWidth,
textureHeight,
/*colorAttachmentFormat=*/vkhpp::Format::eR8G8B8A8Unorm));
const vkhpp::Sampler descriptorSet0Binding0Sampler = *sampledImage.imageSampler;
const std::vector<vkhpp::DescriptorSetLayoutBinding> descriptorSet0Bindings =
{
vkhpp::DescriptorSetLayoutBinding{
.binding = 0,
.descriptorType = vkhpp::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
.stageFlags = vkhpp::ShaderStageFlagBits::eFragment,
.pImmutableSamplers = &descriptorSet0Binding0Sampler,
},
};
const vkhpp::DescriptorSetLayoutCreateInfo descriptorSet0CreateInfo = {
.bindingCount = static_cast<uint32_t>(descriptorSet0Bindings.size()),
.pBindings = descriptorSet0Bindings.data(),
};
auto descriptorSet0Layout = VK_EXPECT_RV(vk.device().createDescriptorSetLayoutUnique(descriptorSet0CreateInfo));
const std::vector<vkhpp::DescriptorPoolSize> descriptorPoolSizes = {
vkhpp::DescriptorPoolSize{
.type = vkhpp::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
},
};
const vkhpp::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.flags = vkhpp::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
.maxSets = 1,
.poolSizeCount = static_cast<uint32_t>(descriptorPoolSizes.size()),
.pPoolSizes = descriptorPoolSizes.data(),
};
auto descriptorSet0Pool = VK_EXPECT_RV(vk.device().createDescriptorPoolUnique(descriptorPoolCreateInfo));
const vkhpp::DescriptorSetLayout descriptorSet0LayoutHandle = *descriptorSet0Layout;
const vkhpp::DescriptorSetAllocateInfo descriptorSet0AllocateInfo = {
.descriptorPool = *descriptorSet0Pool,
.descriptorSetCount = 1,
.pSetLayouts = &descriptorSet0LayoutHandle,
};
auto descriptorSets = VK_EXPECT_RV(vk.device().allocateDescriptorSetsUnique(descriptorSet0AllocateInfo));
auto descriptorSet0(std::move(descriptorSets[0]));
const vkhpp::DescriptorImageInfo descriptorSet0Binding0ImageInfo = {
.sampler = VK_NULL_HANDLE,
.imageView = *sampledImage.imageView,
.imageLayout = vkhpp::ImageLayout::eShaderReadOnlyOptimal,
};
const std::vector<vkhpp::WriteDescriptorSet> descriptorSet0Writes = {
vkhpp::WriteDescriptorSet{
.dstSet = *descriptorSet0,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vkhpp::DescriptorType::eCombinedImageSampler,
.pImageInfo = &descriptorSet0Binding0ImageInfo,
.pBufferInfo = nullptr,
.pTexelBufferView = nullptr,
},
};
vk.device().updateDescriptorSets(descriptorSet0Writes, {});
const std::vector<vkhpp::DescriptorSetLayout> pipelineLayoutDescriptorSetLayouts = {
*descriptorSet0Layout,
};
const vkhpp::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
.setLayoutCount = static_cast<uint32_t>(pipelineLayoutDescriptorSetLayouts.size()),
.pSetLayouts = pipelineLayoutDescriptorSetLayouts.data(),
};
auto pipelineLayout = VK_EXPECT_RV(vk.device().createPipelineLayoutUnique(pipelineLayoutCreateInfo));
const vkhpp::ShaderModuleCreateInfo vertShaderCreateInfo = {
.codeSize = static_cast<uint32_t>(blitVertShaderSpirv.size()),
.pCode = reinterpret_cast<const uint32_t*>(blitVertShaderSpirv.data()),
};
auto vertShaderModule = VK_EXPECT_RV(vk.device().createShaderModuleUnique(vertShaderCreateInfo));
const vkhpp::ShaderModuleCreateInfo fragShaderCreateInfo = {
.codeSize = static_cast<uint32_t>(blitFragShaderSpirv.size()),
.pCode = reinterpret_cast<const uint32_t*>(blitFragShaderSpirv.data()),
};
auto fragShaderModule = VK_EXPECT_RV(vk.device().createShaderModuleUnique(fragShaderCreateInfo));
const std::vector<vkhpp::PipelineShaderStageCreateInfo> pipelineStages = {
vkhpp::PipelineShaderStageCreateInfo{
.stage = vkhpp::ShaderStageFlagBits::eVertex,
.module = *vertShaderModule,
.pName = "main",
},
vkhpp::PipelineShaderStageCreateInfo{
.stage = vkhpp::ShaderStageFlagBits::eFragment,
.module = *fragShaderModule,
.pName = "main",
},
};
const vkhpp::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = {};
const vkhpp::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo = {
.topology = vkhpp::PrimitiveTopology::eTriangleStrip,
};
const vkhpp::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo = {
.viewportCount = 1,
.pViewports = nullptr,
.scissorCount = 1,
.pScissors = nullptr,
};
const vkhpp::PipelineRasterizationStateCreateInfo pipelineRasterStateCreateInfo = {
.depthClampEnable = VK_FALSE,
.rasterizerDiscardEnable = VK_FALSE,
.polygonMode = vkhpp::PolygonMode::eFill,
.cullMode = {},
.frontFace = vkhpp::FrontFace::eCounterClockwise,
.depthBiasEnable = VK_FALSE,
.depthBiasConstantFactor = 0.0f,
.depthBiasClamp = 0.0f,
.depthBiasSlopeFactor = 0.0f,
.lineWidth = 1.0f,
};
const vkhpp::SampleMask pipelineSampleMask = 65535;
const vkhpp::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = {
.rasterizationSamples = vkhpp::SampleCountFlagBits::e1,
.sampleShadingEnable = VK_FALSE,
.minSampleShading = 1.0f,
.pSampleMask = &pipelineSampleMask,
.alphaToCoverageEnable = VK_FALSE,
.alphaToOneEnable = VK_FALSE,
};
const vkhpp::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo = {
.depthTestEnable = VK_FALSE,
.depthWriteEnable = VK_FALSE,
.depthCompareOp = vkhpp::CompareOp::eLess,
.depthBoundsTestEnable = VK_FALSE,
.stencilTestEnable = VK_FALSE,
.front = {
.failOp = vkhpp::StencilOp::eKeep,
.passOp = vkhpp::StencilOp::eKeep,
.depthFailOp = vkhpp::StencilOp::eKeep,
.compareOp = vkhpp::CompareOp::eAlways,
.compareMask = 0,
.writeMask = 0,
.reference = 0,
},
.back = {
.failOp = vkhpp::StencilOp::eKeep,
.passOp = vkhpp::StencilOp::eKeep,
.depthFailOp = vkhpp::StencilOp::eKeep,
.compareOp = vkhpp::CompareOp::eAlways,
.compareMask = 0,
.writeMask = 0,
.reference = 0,
},
.minDepthBounds = 0.0f,
.maxDepthBounds = 0.0f,
};
const std::vector<vkhpp::PipelineColorBlendAttachmentState> pipelineColorBlendAttachments = {
vkhpp::PipelineColorBlendAttachmentState{
.blendEnable = VK_FALSE,
.srcColorBlendFactor = vkhpp::BlendFactor::eOne,
.dstColorBlendFactor = vkhpp::BlendFactor::eOneMinusSrcAlpha,
.colorBlendOp = vkhpp::BlendOp::eAdd,
.srcAlphaBlendFactor = vkhpp::BlendFactor::eOne,
.dstAlphaBlendFactor = vkhpp::BlendFactor::eOneMinusSrcAlpha,
.alphaBlendOp = vkhpp::BlendOp::eAdd,
.colorWriteMask = vkhpp::ColorComponentFlagBits::eR |
vkhpp::ColorComponentFlagBits::eG |
vkhpp::ColorComponentFlagBits::eB |
vkhpp::ColorComponentFlagBits::eA,
},
};
const vkhpp::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo = {
.logicOpEnable = VK_FALSE,
.logicOp = vkhpp::LogicOp::eCopy,
.attachmentCount = static_cast<uint32_t>(pipelineColorBlendAttachments.size()),
.pAttachments = pipelineColorBlendAttachments.data(),
.blendConstants = {{
0.0f,
0.0f,
0.0f,
0.0f,
}},
};
const std::vector<vkhpp::DynamicState> pipelineDynamicStates = {
vkhpp::DynamicState::eViewport,
vkhpp::DynamicState::eScissor,
};
const vkhpp::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = {
.dynamicStateCount = static_cast<uint32_t>(pipelineDynamicStates.size()),
.pDynamicStates = pipelineDynamicStates.data(),
};
const vkhpp::GraphicsPipelineCreateInfo pipelineCreateInfo = {
.stageCount = static_cast<uint32_t>(pipelineStages.size()),
.pStages = pipelineStages.data(),
.pVertexInputState = &pipelineVertexInputStateCreateInfo,
.pInputAssemblyState = &pipelineInputAssemblyStateCreateInfo,
.pTessellationState = nullptr,
.pViewportState = &pipelineViewportStateCreateInfo,
.pRasterizationState = &pipelineRasterStateCreateInfo,
.pMultisampleState = &pipelineMultisampleStateCreateInfo,
.pDepthStencilState = &pipelineDepthStencilStateCreateInfo,
.pColorBlendState = &pipelineColorBlendStateCreateInfo,
.pDynamicState = &pipelineDynamicStateCreateInfo,
.layout = *pipelineLayout,
.renderPass = *framebuffer.renderpass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
};
auto pipeline = VK_EXPECT_RV(vk.device().createGraphicsPipelineUnique({}, pipelineCreateInfo));
VK_EXPECT_RESULT(vk.DoCommandsImmediate(
[&](vkhpp::UniqueCommandBuffer& cmd) {
const std::vector<vkhpp::ClearValue> renderPassBeginClearValues = {
vkhpp::ClearValue{
.color = {
.float32 = {{
1.0f,
0.0f,
0.0f,
1.0f,
}},
},
},
};
const vkhpp::RenderPassBeginInfo renderPassBeginInfo = {
.renderPass = *framebuffer.renderpass,
.framebuffer = *framebuffer.framebuffer,
.renderArea = {
.offset = {
.x = 0,
.y = 0,
},
.extent = {
.width = textureWidth,
.height = textureHeight,
},
},
.clearValueCount = static_cast<uint32_t>(renderPassBeginClearValues.size()),
.pClearValues = renderPassBeginClearValues.data(),
};
cmd->beginRenderPass(renderPassBeginInfo, vkhpp::SubpassContents::eInline);
cmd->bindPipeline(vkhpp::PipelineBindPoint::eGraphics, *pipeline);
cmd->bindDescriptorSets(vkhpp::PipelineBindPoint::eGraphics,
*pipelineLayout,
/*firstSet=*/0, {*descriptorSet0},
/*dynamicOffsets=*/{});
const vkhpp::Viewport viewport = {
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(textureWidth),
.height = static_cast<float>(textureHeight),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
cmd->setViewport(0, {viewport});
const vkhpp::Rect2D scissor = {
.offset = {
.x = 0,
.y = 0,
},
.extent = {
.width = textureWidth,
.height = textureHeight,
},
};
cmd->setScissor(0, {scissor});
cmd->draw(4, 1, 0, 0);
cmd->endRenderPass();
return vkhpp::Result::eSuccess;
}));
const std::vector<uint8_t> renderedPixels = VK_EXPECT(vk.DownloadImage(
textureWidth,
textureHeight,
framebuffer.colorAttachment->image,
vkhpp::ImageLayout::eColorAttachmentOptimal,
vkhpp::ImageLayout::eColorAttachmentOptimal));
#if 0
SaveRGBAToBitmapFile(textureWidth,
textureHeight,
renderedPixels.data(),
"rendered.bmp");
#endif
const RGBAImage actual = {
.width = textureWidth,
.height = textureHeight,
.pixels = std::move(renderedPixels),
};
auto result = CompareImages(textureDataRgba, actual);
return result.ok();
}
} // namespace
gfxstream::expected<Ok, std::string>
PopulateVulkanPrecisionQualifiersOnYuvSamplersQuirk(
::gfxstream::proto::GraphicsAvailability* availability) {
struct ShaderCombo {
std::string name;
const std::vector<uint8_t>& vert;
const std::vector<uint8_t>& frag;
};
const std::vector<ShaderCombo> combos = {
ShaderCombo{
.name = "sampler2D has no precision qualifier",
.vert = kBlitTextureVert,
.frag = kBlitTextureFrag,
},
ShaderCombo{
.name = "sampler2D has a 'lowp' precision qualifier",
.vert = kBlitTextureVert,
.frag = kBlitTextureLowpFrag,
},
ShaderCombo{
.name = "sampler2D has a 'mediump' precision qualifier",
.vert = kBlitTextureVert,
.frag = kBlitTextureMediumpFrag,
},
ShaderCombo{
.name = "sampler2D has a 'highp' precision qualifier",
.vert = kBlitTextureVert,
.frag = kBlitTextureHighpFrag,
},
};
bool anyTestFailed = false;
for (const auto& combo : combos) {
auto result = CanHandlePrecisionQualifierWithYuvSampler(combo.vert, combo.frag);
if (!result.ok()) {
// Failed to run to completion.
return gfxstream::unexpected(vkhpp::to_string(result.error()));
}
const bool passedTest = result.value();
if (!passedTest) {
// Ran to completion but had bad value.
anyTestFailed = true;
break;
}
}
// TODO: Run this test per device.
availability->mutable_vulkan()
->mutable_physical_devices(0)
->mutable_quirks()
->set_has_issue_with_precision_qualifiers_on_yuv_samplers(anyTestFailed);
return Ok{};
}
} // namespace gfxstream