diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 5e5c9641b..f609c4cd0 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -711,17 +711,57 @@ QVariantList VulkanPipelineStateViewer::makeSampler(const QString &bindset, cons .arg((descriptor.minLOD == -FLT_MAX ? lit("0") : QString::number(descriptor.minLOD))) .arg((descriptor.maxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(descriptor.maxLOD))); + // omit lod clamp if this is an immutable sampler and the attached resource is entirely within the + // range + if(descriptor.immutableSampler) + { + TextureDescription *tex = m_Ctx.GetTexture(descriptor.resourceResourceId); + if(tex && descriptor.minLOD <= 0.0f && descriptor.maxLOD >= (float)(tex->mips - 1)) + { + lod = QString(); + } + } + if(descriptor.mipBias != 0.0f) lod += lit(" Bias %1").arg(descriptor.mipBias); - return {QString(), - bindset, - slotname, - descriptor.immutableSampler ? tr("Immutable Sampler") : tr("Sampler"), - descriptor.samplerResourceId, - addressing, - filter + lit(", ") + lod, - QString()}; + if(!lod.isEmpty()) + lod = lit(", ") + lod; + + QString obj = ToQStr(descriptor.samplerResourceId); + + if(descriptor.ycbcrSampler != ResourceId()) + { + obj += lit(" ") + ToQStr(descriptor.ycbcrSampler); + + if(descriptor.ycbcrSwizzle[0] != TextureSwizzle::Red || + descriptor.ycbcrSwizzle[1] != TextureSwizzle::Green || + descriptor.ycbcrSwizzle[2] != TextureSwizzle::Blue || + descriptor.ycbcrSwizzle[3] != TextureSwizzle::Alpha) + { + obj += tr(" swizzle[%1%2%3%4]") + .arg(ToQStr(descriptor.swizzle[0])) + .arg(ToQStr(descriptor.swizzle[1])) + .arg(ToQStr(descriptor.swizzle[2])) + .arg(ToQStr(descriptor.swizzle[3])); + } + + filter += + QFormatStr(", %1 %2").arg(ToQStr(descriptor.ycbcrModel)).arg(ToQStr(descriptor.ycbcrRange)); + + addressing += tr(", Chroma %1 [%2,%3]") + .arg(ToQStr(descriptor.chromaFilter)) + .arg(ToQStr(descriptor.xChromaOffset)) + .arg(ToQStr(descriptor.yChromaOffset)); + + if(descriptor.forceExplicitReconstruction) + addressing += tr(" Explicit"); + } + + return {QString(), bindset, + slotname, descriptor.immutableSampler ? tr("Immutable Sampler") : tr("Sampler"), + obj, addressing, + filter + lod, QString()}; } void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, diff --git a/renderdoc/api/replay/renderdoc_tostr.inl b/renderdoc/api/replay/renderdoc_tostr.inl index c1d819304..562c6fd70 100644 --- a/renderdoc/api/replay/renderdoc_tostr.inl +++ b/renderdoc/api/replay/renderdoc_tostr.inl @@ -439,6 +439,42 @@ std::string DoStringise(const AddressMode &el) END_ENUM_STRINGISE(); } +template <> +std::string DoStringise(const YcbcrConversion &el) +{ + BEGIN_ENUM_STRINGISE(YcbcrConversion); + { + STRINGISE_ENUM_CLASS(Raw); + STRINGISE_ENUM_CLASS_NAMED(RangeOnly, "Range Only"); + STRINGISE_ENUM_CLASS_NAMED(BT709, "BT.709"); + STRINGISE_ENUM_CLASS_NAMED(BT601, "BT.601"); + STRINGISE_ENUM_CLASS_NAMED(BT2020, "BT.2020"); + } + END_ENUM_STRINGISE(); +} + +template <> +std::string DoStringise(const YcbcrRange &el) +{ + BEGIN_ENUM_STRINGISE(YcbcrRange); + { + STRINGISE_ENUM_CLASS_NAMED(ITUFull, "Full"); + STRINGISE_ENUM_CLASS_NAMED(ITUNarrow, "Narrow"); + } + END_ENUM_STRINGISE(); +} + +template <> +std::string DoStringise(const ChromaSampleLocation &el) +{ + BEGIN_ENUM_STRINGISE(ChromaSampleLocation); + { + STRINGISE_ENUM_CLASS_NAMED(CositedEven, "Even"); + STRINGISE_ENUM_CLASS_NAMED(Midpoint, "Mid"); + } + END_ENUM_STRINGISE(); +} + template <> std::string DoStringise(const ResourceType &el) { diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 0a38f8166..258dc8c77 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -315,6 +315,33 @@ enum class AddressMode : uint32_t DECLARE_REFLECTION_ENUM(AddressMode); +enum YcbcrConversion +{ + Raw, + RangeOnly, + BT709, + BT601, + BT2020, +}; + +DECLARE_REFLECTION_ENUM(YcbcrConversion); + +enum YcbcrRange +{ + ITUFull, + ITUNarrow, +}; + +DECLARE_REFLECTION_ENUM(YcbcrRange); + +enum ChromaSampleLocation +{ + CositedEven, + Midpoint, +}; + +DECLARE_REFLECTION_ENUM(ChromaSampleLocation); + DOCUMENT(R"(The type of a resource referred to by binding or API usage. In some cases there is a little overlap or fudging when mapping API concepts - this is primarily diff --git a/renderdoc/api/replay/vk_pipestate.h b/renderdoc/api/replay/vk_pipestate.h index 5418db076..81f096b96 100644 --- a/renderdoc/api/replay/vk_pipestate.h +++ b/renderdoc/api/replay/vk_pipestate.h @@ -166,6 +166,28 @@ struct BindingElement DOCUMENT("For samplers - ``True`` if unnormalized co-ordinates are used in this sampler."); bool unnormalized = false; + DOCUMENT(R"(For samplers - the :class:`ResourceId` of the ycbcr conversion object associated with +this sampler. +)"); + ResourceId ycbcrSampler; + + DOCUMENT("For ycbcr samplers - the :class:`YcbcrConversion` used for conversion."); + YcbcrConversion ycbcrModel; + DOCUMENT("For ycbcr samplers - the :class:`YcbcrRange` used for conversion."); + YcbcrRange ycbcrRange; + DOCUMENT(R"(For ycbcr samplers - Four :class:`TextureSwizzle` elements indicating the swizzle +applied before conversion. +)"); + TextureSwizzle ycbcrSwizzle[4]; + DOCUMENT("For ycbcr samplers - the :class:`ChromaSampleLocation` X-axis chroma offset."); + ChromaSampleLocation xChromaOffset; + DOCUMENT("For ycbcr samplers - the :class:`ChromaSampleLocation` Y-axis chroma offset."); + ChromaSampleLocation yChromaOffset; + DOCUMENT("For ycbcr samplers - the :class:`FilterMode` describing the chroma filtering mode."); + FilterMode chromaFilter; + DOCUMENT("For ycbcr samplers - ``True`` if explicit reconstruction is force enabled."); + bool forceExplicitReconstruction; + DOCUMENT(R"(For samplers - check if the border color is used in this Vulkan sampler. :return: ``True`` if the border color is used, ``False`` otherwise. diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 712c02e93..720a072fb 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -522,7 +522,7 @@ CompareFunction MakeCompareFunc(VkCompareOp func) return CompareFunction::AlwaysTrue; } -static FilterMode MakeFilterMode(VkFilter f) +FilterMode MakeFilterMode(VkFilter f) { switch(f) { diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index 7d0de71a1..0dd9bfa9a 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -91,6 +91,7 @@ VkPrimitiveTopology MakeVkPrimitiveTopology(Topology Topo); AddressMode MakeAddressMode(VkSamplerAddressMode addr); void MakeBorderColor(VkBorderColor border, FloatVector *BorderColor); CompareFunction MakeCompareFunc(VkCompareOp func); +FilterMode MakeFilterMode(VkFilter f); TextureFilter MakeFilter(VkFilter minFilter, VkFilter magFilter, VkSamplerMipmapMode mipmapMode, bool anisoEnable, bool compareEnable, VkSamplerReductionModeEXT reduction); LogicOperation MakeLogicOp(VkLogicOp op); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index c1a6e8ded..35caf3519 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -2635,6 +2635,10 @@ bool WrappedVulkan::ProcessChunk(ReadSerialiser &ser, VulkanChunk chunk) return Serialise_vkCmdInsertDebugUtilsLabelEXT(ser, VK_NULL_HANDLE, NULL); break; + case VulkanChunk::vkCreateSamplerYcbcrConversion: + return Serialise_vkCreateSamplerYcbcrConversion(ser, VK_NULL_HANDLE, NULL, NULL, NULL); + break; + case VulkanChunk::vkCmdSetDeviceMask: return Serialise_vkCmdSetDeviceMask(ser, VK_NULL_HANDLE, 0); break; diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 625926eed..66307b5d1 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -738,12 +738,14 @@ void VulkanCreationInfo::Sampler::Init(VulkanResourceManager *resourceMan, Vulka { reductionMode = reduction->reductionMode; } -} -void VulkanCreationInfo::YCbCrSampler::Init(VulkanResourceManager *resourceMan, - VulkanCreationInfo &info, - const VkSamplerYcbcrConversionCreateInfo *pCreateInfo) -{ + const VkSamplerYcbcrConversionInfo *ycbcrInfo = + (const VkSamplerYcbcrConversionInfo *)FindNextStruct( + pCreateInfo, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO); + if(ycbcrInfo) + { + ycbcr = GetResID(ycbcrInfo->conversion); + } } static TextureSwizzle Convert(VkComponentSwizzle s, int i) @@ -763,6 +765,55 @@ static TextureSwizzle Convert(VkComponentSwizzle s, int i) return TextureSwizzle(uint32_t(TextureSwizzle::Red) + i); } +void VulkanCreationInfo::YCbCrSampler::Init(VulkanResourceManager *resourceMan, + VulkanCreationInfo &info, + const VkSamplerYcbcrConversionCreateInfo *pCreateInfo) +{ + switch(pCreateInfo->ycbcrModel) + { + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY: ycbcrModel = YcbcrConversion::Raw; break; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY: + ycbcrModel = YcbcrConversion::RangeOnly; + break; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709: ycbcrModel = YcbcrConversion::BT709; break; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601: ycbcrModel = YcbcrConversion::BT601; break; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020: ycbcrModel = YcbcrConversion::BT2020; break; + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_MAX_ENUM: + case VK_SAMPLER_YCBCR_MODEL_CONVERSION_RANGE_SIZE: break; + } + + switch(pCreateInfo->ycbcrRange) + { + case VK_SAMPLER_YCBCR_RANGE_ITU_FULL: ycbcrRange = YcbcrRange::ITUFull; break; + case VK_SAMPLER_YCBCR_RANGE_ITU_NARROW: ycbcrRange = YcbcrRange::ITUNarrow; break; + case VK_SAMPLER_YCBCR_RANGE_MAX_ENUM: + case VK_SAMPLER_YCBCR_RANGE_RANGE_SIZE: break; + } + + switch(pCreateInfo->xChromaOffset) + { + case VK_CHROMA_LOCATION_COSITED_EVEN: xChromaOffset = ChromaSampleLocation::CositedEven; break; + case VK_CHROMA_LOCATION_MIDPOINT: xChromaOffset = ChromaSampleLocation::Midpoint; break; + case VK_CHROMA_LOCATION_MAX_ENUM: + case VK_CHROMA_LOCATION_RANGE_SIZE: break; + } + + switch(pCreateInfo->yChromaOffset) + { + case VK_CHROMA_LOCATION_COSITED_EVEN: yChromaOffset = ChromaSampleLocation::CositedEven; break; + case VK_CHROMA_LOCATION_MIDPOINT: yChromaOffset = ChromaSampleLocation::Midpoint; break; + case VK_CHROMA_LOCATION_MAX_ENUM: + case VK_CHROMA_LOCATION_RANGE_SIZE: break; + } + + swizzle[0] = Convert(pCreateInfo->components.r, 0); + swizzle[1] = Convert(pCreateInfo->components.g, 1); + swizzle[2] = Convert(pCreateInfo->components.b, 2); + swizzle[3] = Convert(pCreateInfo->components.a, 3); + chromaFilter = MakeFilterMode(pCreateInfo->chromaFilter); + forceExplicitReconstruction = pCreateInfo->forceExplicitReconstruction != 0; +} + void VulkanCreationInfo::ImageView::Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, const VkImageViewCreateInfo *pCreateInfo) { diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index 916767618..fab8388c6 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -386,6 +386,8 @@ struct VulkanCreationInfo VkBorderColor borderColor; bool unnormalizedCoordinates; VkSamplerReductionModeEXT reductionMode; + + ResourceId ycbcr; }; map m_Sampler; @@ -393,6 +395,14 @@ struct VulkanCreationInfo { void Init(VulkanResourceManager *resourceMan, VulkanCreationInfo &info, const VkSamplerYcbcrConversionCreateInfo *pCreateInfo); + + YcbcrConversion ycbcrModel; + YcbcrRange ycbcrRange; + TextureSwizzle swizzle[4]; + ChromaSampleLocation xChromaOffset; + ChromaSampleLocation yChromaOffset; + FilterMode chromaFilter; + bool forceExplicitReconstruction; }; map m_YCbCrSampler; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 99d0397d5..e8d4f514e 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -1414,6 +1414,20 @@ void VulkanReplay::SavePipelineState() el.maxLOD = sampl.maxLod; MakeBorderColor(sampl.borderColor, (FloatVector *)el.borderColor); el.unnormalized = sampl.unnormalizedCoordinates; + + if(sampl.ycbcr != ResourceId()) + { + const VulkanCreationInfo::YCbCrSampler &ycbcr = c.m_YCbCrSampler[sampl.ycbcr]; + el.ycbcrSampler = rm->GetOriginalID(sampl.ycbcr); + + el.ycbcrModel = ycbcr.ycbcrModel; + el.ycbcrRange = ycbcr.ycbcrRange; + memcpy(el.ycbcrSwizzle, ycbcr.swizzle, sizeof(TextureSwizzle) * 4); + el.xChromaOffset = ycbcr.xChromaOffset; + el.yChromaOffset = ycbcr.yChromaOffset; + el.chromaFilter = ycbcr.chromaFilter; + el.forceExplicitReconstruction = ycbcr.forceExplicitReconstruction; + } } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp index 5b9dbc4ef..5419e578a 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp @@ -551,17 +551,6 @@ void WrappedVulkan::vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice { ObjDisp(physicalDevice)->GetPhysicalDeviceFeatures2(Unwrap(physicalDevice), pFeatures); - // if the user is requesting ycbcr features, make sure it's reported as NOT supported - VkPhysicalDeviceSamplerYcbcrConversionFeatures *ycbcr = - (VkPhysicalDeviceSamplerYcbcrConversionFeatures *)FindNextStruct( - pFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES); - - if(ycbcr) - { - RDCWARN("Forcibly disabling support for YCbCr Conversion"); - ycbcr->samplerYcbcrConversion = VK_FALSE; - } - // if the user is requesting protected memory, make sure it's reported as NOT supported VkPhysicalDeviceProtectedMemoryFeatures *protectedMem = (VkPhysicalDeviceProtectedMemoryFeatures *)FindNextStruct( diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 46509e003..7ad71f520 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -466,6 +466,12 @@ bool WrappedVulkan::Serialise_vkCreateSampler(SerialiserType &ser, VkDevice devi AddResource(Sampler, ResourceType::Sampler, "Sampler"); DerivedResource(device, Sampler); + const VkSamplerYcbcrConversionInfo *ycbcr = (const VkSamplerYcbcrConversionInfo *)FindNextStruct( + &CreateInfo, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO); + if(ycbcr) + { + DerivedResource(ycbcr->conversion, Sampler); + } } return true; @@ -503,6 +509,15 @@ VkResult WrappedVulkan::vkCreateSampler(VkDevice device, const VkSamplerCreateIn VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pSampler); record->AddChunk(chunk); + + const VkSamplerYcbcrConversionInfo *ycbcr = + (const VkSamplerYcbcrConversionInfo *)FindNextStruct( + pCreateInfo, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO); + if(ycbcr) + { + VkResourceRecord *ycbcrRecord = GetRecord(ycbcr->conversion); + record->AddParent(ycbcrRecord); + } } else { diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 4bbaab948..c9163cbcb 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -1800,7 +1800,17 @@ void DoSerialise(SerialiserType &ser, VKPipe::BindingElement &el) SERIALISE_MEMBER(borderColor); SERIALISE_MEMBER(unnormalized); - SIZE_CHECK(152); + SERIALISE_MEMBER(ycbcrSampler); + + SERIALISE_MEMBER(ycbcrModel); + SERIALISE_MEMBER(ycbcrRange); + SERIALISE_MEMBER(ycbcrSwizzle); + SERIALISE_MEMBER(xChromaOffset); + SERIALISE_MEMBER(yChromaOffset); + SERIALISE_MEMBER(chromaFilter); + SERIALISE_MEMBER(forceExplicitReconstruction); + + SIZE_CHECK(200); }; template diff --git a/util/test/demos/vk/vk_video_textures.cpp b/util/test/demos/vk/vk_video_textures.cpp index d8e267a52..b578b3a5d 100644 --- a/util/test/demos/vk/vk_video_textures.cpp +++ b/util/test/demos/vk/vk_video_textures.cpp @@ -791,7 +791,7 @@ void main() VkPipeline pipe = VK_NULL_HANDLE; VkPipelineLayout layout = VK_NULL_HANDLE; VkDescriptorSet descset = VK_NULL_HANDLE; - } ycbcr[2]; + } ycbcr[6]; VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcrFeats = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES}; @@ -808,6 +808,9 @@ void main() { createInfo.chromaFilter = VK_FILTER_LINEAR; createInfo.format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM; + createInfo.xChromaOffset = VK_CHROMA_LOCATION_MIDPOINT; + createInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT; + createInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020; createInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL; @@ -818,7 +821,31 @@ void main() createInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW; vkCreateSamplerYcbcrConversionKHR(device, &createInfo, NULL, &ycbcr[1].conv); - ycbcr[0].name = "YCbCr 601 Narrow"; + ycbcr[1].name = "YCbCr 601 Narrow"; + + createInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; + createInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW; + + vkCreateSamplerYcbcrConversionKHR(device, &createInfo, NULL, &ycbcr[2].conv); + ycbcr[2].name = "RGB Identity Narrow"; + + createInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; + createInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL; + + vkCreateSamplerYcbcrConversionKHR(device, &createInfo, NULL, &ycbcr[3].conv); + ycbcr[3].name = "RGB Identity Full"; + + createInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY; + createInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW; + + vkCreateSamplerYcbcrConversionKHR(device, &createInfo, NULL, &ycbcr[4].conv); + ycbcr[4].name = "YCbCr Identity Narrow"; + + createInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY; + createInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL; + + vkCreateSamplerYcbcrConversionKHR(device, &createInfo, NULL, &ycbcr[5].conv); + ycbcr[5].name = "YCbCr Identity Full"; pipeCreateInfo.stages = { CompileShaderModule(common + vertex, ShaderLang::glsl, ShaderStage::vert, "main"), @@ -933,7 +960,7 @@ void main() vkCmdDraw(cmd, 4, 1, 0, 0); } - x += 100.0f; + x += 60.0f; } vkCmdEndRenderPass(cmd);