diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 431851d7..325679a8 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -190,6 +190,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/gpu/interconnect/inline2memory.cpp ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/active_state.cpp ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp + ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.cpp ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.cpp ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp ${source_DIR}/skyline/gpu/interconnect/maxwell_3d/constant_buffers.cpp @@ -205,6 +206,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/gpu/interconnect/common/samplers.cpp ${source_DIR}/skyline/gpu/interconnect/common/textures.cpp ${source_DIR}/skyline/gpu/interconnect/common/shader_cache.cpp + ${source_DIR}/skyline/gpu/interconnect/common/pipeline_state_bundle.cpp ${source_DIR}/skyline/gpu/shaders/helper_shaders.cpp ${source_DIR}/skyline/soc/smmu.cpp ${source_DIR}/skyline/soc/host1x/syncpoint.cpp diff --git a/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_accessor.h b/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_accessor.h new file mode 100644 index 00000000..9ef3c2dc --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_accessor.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include "common.h" +#include "textures.h" + +namespace skyline::gpu::interconnect { + /** + * @brief Provides an abstract interface for accessing pipeline state at creation time + */ + class PipelineStateAccessor { + public: + /** + * @return The texture type for the TIC entry at the given index + */ + virtual Shader::TextureType GetTextureType(u32 index) const = 0; + + /** + * @return The value of the constant buffer at the given index, offset and stage + */ + virtual u32 GetConstantBufferValue(u32 shaderStage, u32 index, u32 offset) const = 0; + + /** + * @return The raw binary for the given pipeline stage + */ + virtual ShaderBinary GetShaderBinary(u32 pipelineStage) const = 0; + + /** + * @brief Marks that all Get* operations on the pipeline state has finished and the pipeline is build + */ + virtual void MarkComplete() const = 0; + + virtual ~PipelineStateAccessor() = default; + }; +} diff --git a/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_bundle.cpp b/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_bundle.cpp new file mode 100644 index 00000000..97954629 --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_bundle.cpp @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include "pipeline_state_bundle.h" + +namespace skyline::gpu::interconnect { + void PipelineStateBundle::PipelineStage::Reset() { + binary.clear(); + binaryBaseOffset = 0; + } + + PipelineStateBundle::PipelineStateBundle() {} + + void PipelineStateBundle::Reset(span newKey) { + ranges::for_each(pipelineStages, [](auto &stage) { stage.Reset(); }); + key.resize(newKey.size()); + span(key).copy_from(newKey); + textureTypes.clear(); + constantBufferValues.clear(); + } + + void PipelineStateBundle::SetShaderBinary(u32 pipelineStage, ShaderBinary bin) { + if (pipelineStages.size() <= pipelineStage) + pipelineStages.resize(pipelineStage + 1); + auto &stageInfo{pipelineStages[pipelineStage]}; + stageInfo.binary.resize(bin.binary.size()); + span(stageInfo.binary).copy_from(bin.binary); + stageInfo.binaryBaseOffset = bin.baseOffset; + } + + void PipelineStateBundle::AddTextureType(u32 index, Shader::TextureType type) { + textureTypes.emplace_back(index, type); + } + + void PipelineStateBundle::AddConstantBufferValue(u32 shaderStage, u32 index, u32 offset, u32 value) { + constantBufferValues.push_back({shaderStage, index, offset, value}); + } + + span PipelineStateBundle::GetKey() { + return span(key); + } + + ShaderBinary PipelineStateBundle::GetShaderBinary(u32 pipelineStage) { + auto &stageInfo{pipelineStages[pipelineStage]}; + return {stageInfo.binary, stageInfo.binaryBaseOffset}; + } + + Shader::TextureType PipelineStateBundle::LookupTextureType(u32 offset) { + auto it{ranges::find_if(textureTypes, [offset](auto &pair) { return pair.first == offset; })}; + if (it == textureTypes.end()) + throw exception("Failed to find texture type for offset: 0x{:X}", offset); + + return it->second; + } + + u32 PipelineStateBundle::LookupConstantBufferValue(u32 shaderStage, u32 index, u32 offset) { + auto it{ranges::find_if(constantBufferValues, [index, offset, shaderStage](auto &val) { return val.index == index && val.offset == offset && val.shaderStage == shaderStage; })}; + if (it == constantBufferValues.end()) + throw exception("Failed to find constant buffer value for offset: 0x{:X}", offset); + + return it->value; + } +} diff --git a/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_bundle.h b/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_bundle.h new file mode 100644 index 00000000..fec50f4d --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/common/pipeline_state_bundle.h @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include "common.h" + +namespace skyline::gpu::interconnect { + /** + * @brief Stores both key and non-key state for a pipeline that is otherwise only accessible at creation time + */ + class PipelineStateBundle { + private: + std::vector key; //!< Byte array containing the pipeline key, this is interpreted by the the user and two different keys might refer to the same pipeline + + /** + * @brief Holds the raw binary and associated info for a pipeline stage + */ + struct PipelineStage { + std::vector binary; + u32 binaryBaseOffset; + + void Reset(); + }; + + /** + * @brief Holds a value of a constant buffer read from memory at pipeline creation time + */ + struct ConstantBufferValue { + u32 shaderStage; + u32 index; + u32 offset; + u32 value; + }; + + boost::container::small_vector constantBufferValues; + boost::container::small_vector, 4> textureTypes; + + std::vector pipelineStages{}; + + public: + PipelineStateBundle(); + + /** + * @brief Resets the bundle's state using the given key so it can be reused for a new pipeline + */ + void Reset(span newKey); + + template requires std::is_trivially_copyable_v + void Reset(const T &value) { + Reset(span(reinterpret_cast(&value), sizeof(T))); + } + + /** + * @brief Sets the binary for a given pipeline stage + */ + void SetShaderBinary(u32 pipelineStage, ShaderBinary bin); + + /** + * @brief Adds a texture type value for a given offset to the bundle + */ + void AddTextureType(u32 index, Shader::TextureType type); + + /** + * @brief Adds a constant buffer value for a given offset and shader stage to the bundle + */ + void AddConstantBufferValue(u32 shaderStage, u32 index, u32 offset, u32 value); + + /** + * @brief Returns the raw key data for the pipeline + */ + span GetKey(); + + template requires std::is_trivially_copyable_v + const T &GetKey() { + return *reinterpret_cast(key.data()); + } + + /** + * @brief Returns the binary for a given pipeline stage + */ + ShaderBinary GetShaderBinary(u32 pipelineStage); + + /** + * @brief Returns the texture type for a given offset + */ + Shader::TextureType LookupTextureType(u32 offset); + + /** + * @brief Returns the constant buffer value for a given offset and shader stage + */ + u32 LookupConstantBufferValue(u32 shaderStage, u32 index, u32 offset); + }; +} diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.cpp new file mode 100644 index 00000000..e67adf3f --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.cpp @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include "graphics_pipeline_state_accessor.h" + +namespace skyline::gpu::interconnect::maxwell3d { + RuntimeGraphicsPipelineStateAccessor::RuntimeGraphicsPipelineStateAccessor(PipelineStateBundle &bundle, + InterconnectContext &ctx, + Textures &textures, ConstantBufferSet &constantBuffers, + const std::array &shaderBinaries) + : bundle{bundle}, ctx{ctx}, textures{textures}, constantBuffers{constantBuffers}, shaderBinaries{shaderBinaries} {} + + + Shader::TextureType RuntimeGraphicsPipelineStateAccessor::GetTextureType(u32 index) const { + Shader::TextureType type{textures.GetTextureType(ctx, index)}; + bundle.AddTextureType(index, type); + return type; + } + + u32 RuntimeGraphicsPipelineStateAccessor::GetConstantBufferValue(u32 shaderStage, u32 index, u32 offset) const { + u32 value{constantBuffers[shaderStage][index].Read(ctx.executor, offset)}; + bundle.AddConstantBufferValue(shaderStage, index, offset, value); + return value; + } + + ShaderBinary RuntimeGraphicsPipelineStateAccessor::GetShaderBinary(u32 pipelineStage) const { + ShaderBinary binary{shaderBinaries[pipelineStage]}; + bundle.SetShaderBinary(pipelineStage, binary); + return binary; + } + + void RuntimeGraphicsPipelineStateAccessor::MarkComplete() const {} +} diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.h b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.h new file mode 100644 index 00000000..cc55e7d2 --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/graphics_pipeline_state_accessor.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include +#include "constant_buffers.h" + + +namespace skyline::gpu::interconnect::maxwell3d { + /** + * @brief Implements the PipelineStateAccessor interface for pipelines created at emulator runtime + */ + class RuntimeGraphicsPipelineStateAccessor : public PipelineStateAccessor { + private: + PipelineStateBundle &bundle; + InterconnectContext &ctx; + Textures &textures; + ConstantBufferSet &constantBuffers; + std::array shaderBinaries; + + public: + RuntimeGraphicsPipelineStateAccessor(PipelineStateBundle &bundle, + InterconnectContext &ctx, + Textures &textures, ConstantBufferSet &constantBuffers, + const std::array &shaderBinaries); + + Shader::TextureType GetTextureType(u32 index) const override; + + u32 GetConstantBufferValue(u32 shaderStage, u32 index, u32 offset) const override; + + ShaderBinary GetShaderBinary(u32 pipelineStage) const override; + + void MarkComplete() const override; + }; +} diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp index b5127bce..d5e234cb 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp @@ -173,35 +173,36 @@ namespace skyline::gpu::interconnect::maxwell3d { return info; } - static std::array MakePipelineShaders(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array &shaderBinaries) { + static std::array MakePipelineShaders(InterconnectContext &ctx, const PipelineStateAccessor &accessor, const PackedPipelineState &packedState) { ctx.gpu.shader.ResetPools(); using PipelineStage = engine::Pipeline::Shader::Type; - auto pipelineStage{[](size_t i) { return static_cast(i); }}; + auto pipelineStage{[](u32 i) { return static_cast(i); }}; auto stageIdx{[](PipelineStage stage) { return static_cast(stage); }}; std::array programs; bool ignoreVertexCullBeforeFetch{}; - for (size_t i{}; i < engine::PipelineCount; i++) { + for (u32 i{}; i < engine::PipelineCount; i++) { if (!packedState.shaderHashes[i]) continue; + auto binary{accessor.GetShaderBinary(i)}; auto program{ctx.gpu.shader.ParseGraphicsShader( packedState.postVtgShaderAttributeSkipMask, ConvertCompilerShaderStage(static_cast(i)), - shaderBinaries[i].binary, shaderBinaries[i].baseOffset, + binary.binary, binary.baseOffset, packedState.bindlessTextureConstantBufferSlotSelect, packedState.viewportTransformEnable, [&](u32 index, u32 offset) { - size_t shaderStage{i > 0 ? (i - 1) : 0}; - return constantBuffers[shaderStage][index].Read(ctx.executor, offset); + u32 shaderStage{i > 0 ? (i - 1) : 0}; + return accessor.GetConstantBufferValue(shaderStage, index, offset); }, [&](u32 index) { - return textures.GetTextureType(ctx, BindlessHandle{ .raw = index }.textureIndex); + return accessor.GetTextureType(BindlessHandle{ .raw = index }.textureIndex); })}; if (i == stageIdx(PipelineStage::Vertex) && packedState.shaderHashes[stageIdx(PipelineStage::VertexCullBeforeFetch)]) { ignoreVertexCullBeforeFetch = true; - programs[i] = ctx.gpu.shader.CombineVertexShaders(programs[stageIdx(PipelineStage::VertexCullBeforeFetch)], program, shaderBinaries[i].binary); + programs[i] = ctx.gpu.shader.CombineVertexShaders(programs[stageIdx(PipelineStage::VertexCullBeforeFetch)], program, binary.binary); } else { programs[i] = program; } @@ -213,7 +214,7 @@ namespace skyline::gpu::interconnect::maxwell3d { std::array shaderStages{}; - for (size_t i{stageIdx(ignoreVertexCullBeforeFetch ? PipelineStage::Vertex : PipelineStage::VertexCullBeforeFetch)}; i < engine::PipelineCount; i++) { + for (u32 i{stageIdx(ignoreVertexCullBeforeFetch ? PipelineStage::Vertex : PipelineStage::VertexCullBeforeFetch)}; i < engine::PipelineCount; i++) { if (!packedState.shaderHashes[i]) continue; @@ -424,8 +425,7 @@ namespace skyline::gpu::interconnect::maxwell3d { static cache::GraphicsPipelineCache::CompiledPipeline MakeCompiledPipeline(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array &shaderStages, - span layoutBindings, - span colorAttachments, TextureView *depthAttachment) { + span layoutBindings) { boost::container::static_vector shaderStageInfos; for (const auto &stage : shaderStages) if (stage.module) @@ -526,8 +526,13 @@ namespace skyline::gpu::interconnect::maxwell3d { std::tie(depthStencilState.front, depthStencilState.back) = packedState.GetStencilOpsState(); boost::container::static_vector attachmentBlendStates; - for (u32 i{}; i < colorAttachments.size(); i++) + boost::container::static_vector colorAttachmentFormats; + + for (u32 i{}; i < packedState.GetColorRenderTargetCount(); i++) { attachmentBlendStates.push_back(packedState.GetAttachmentBlendState(i)); + texture::Format format{packedState.GetColorRenderTargetFormat(i)}; + colorAttachmentFormats.push_back(format ? format->vkFormat : vk::Format::eUndefined); + } vk::PipelineColorBlendStateCreateInfo colorBlendState{ .logicOpEnable = packedState.logicOpEnable, @@ -536,7 +541,11 @@ namespace skyline::gpu::interconnect::maxwell3d { .pAttachments = attachmentBlendStates.data() }; - constexpr std::array dynamicStates{ + + static constexpr u32 BaseDynamicStateCount{9}; + static constexpr u32 ExtendedDynamicStateCount{BaseDynamicStateCount + 1}; + + constexpr std::array dynamicStates{ vk::DynamicState::eViewport, vk::DynamicState::eScissor, vk::DynamicState::eLineWidth, @@ -550,9 +559,6 @@ namespace skyline::gpu::interconnect::maxwell3d { vk::DynamicState::eVertexInputBindingStrideEXT }; - static constexpr u32 BaseDynamicStateCount{9}; - static constexpr u32 ExtendedDynamicStateCount{BaseDynamicStateCount + 1}; - vk::PipelineDynamicStateCreateInfo dynamicState{ .dynamicStateCount = ctx.gpu.traits.supportsExtendedDynamicState ? ExtendedDynamicStateCount : BaseDynamicStateCount, .pDynamicStates = dynamicStates.data() @@ -569,6 +575,8 @@ namespace skyline::gpu::interconnect::maxwell3d { .pScissors = emptyScissors.data(), }; + texture::Format depthStencilFormat{packedState.GetDepthRenderTargetFormat()}; + return ctx.gpu.graphicsPipelineCache.GetCompiledPipeline(cache::GraphicsPipelineCache::PipelineState{ .shaderStages = shaderStageInfos, .vertexState = vertexInputState, @@ -580,15 +588,16 @@ namespace skyline::gpu::interconnect::maxwell3d { .depthStencilState = depthStencilState, .colorBlendState = colorBlendState, .dynamicState = dynamicState, - .colorAttachments = colorAttachments, - .depthStencilAttachment = depthAttachment, + .colorFormats = colorAttachmentFormats, + .depthStencilFormat = depthStencilFormat ? depthStencilFormat->vkFormat : vk::Format::eUndefined, + .sampleCount = vk::SampleCountFlagBits::e1, //TODO: fix after MSAA support }, layoutBindings); } - Pipeline::Pipeline(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array &shaderBinaries, span colorAttachments, TextureView *depthAttachment) - : shaderStages{MakePipelineShaders(ctx, textures, constantBuffers, packedState, shaderBinaries)}, + Pipeline::Pipeline(InterconnectContext &ctx, const PipelineStateAccessor &accessor, const PackedPipelineState &packedState) + : shaderStages{MakePipelineShaders(ctx, accessor, packedState)}, descriptorInfo{MakePipelineDescriptorInfo(shaderStages, ctx.gpu.traits.quirks.needsIndividualTextureBindingWrites)}, - compiledPipeline{MakeCompiledPipeline(ctx, packedState, shaderStages, descriptorInfo.descriptorSetLayoutBindings, colorAttachments, depthAttachment)}, + compiledPipeline{MakeCompiledPipeline(ctx, packedState, shaderStages, descriptorInfo.descriptorSetLayoutBindings)}, sourcePackedState{packedState} { storageBufferViews.resize(descriptorInfo.totalStorageBufferCount); } diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h index 8aaf81ea..eadfea2b 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h @@ -11,6 +11,7 @@ #include "common.h" #include "packed_pipeline_state.h" #include "constant_buffers.h" +#include "graphics_pipeline_state_accessor.h" namespace skyline::gpu { class TextureView; @@ -100,7 +101,7 @@ namespace skyline::gpu::interconnect::maxwell3d { PackedPipelineState sourcePackedState; - Pipeline(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array &shaderBinaries, span colorAttachments, TextureView *depthAttachment); + Pipeline(InterconnectContext &ctx, const PipelineStateAccessor &accessor, const PackedPipelineState &packedState); Pipeline *LookupNext(const PackedPipelineState &packedState); @@ -123,17 +124,23 @@ namespace skyline::gpu::interconnect::maxwell3d { DescriptorUpdateInfo *SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, Samplers &samplers, Textures &textures, ConstantBuffers::QuickBind quickBind, span sampledImages); }; + /** + * @brief Manages the caching and creation of pipelines + */ class PipelineManager { private: + PipelineStateBundle bundle; tsl::robin_map, PackedPipelineStateHash> map; public: - Pipeline *FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array &shaderBinaries, span colorAttachments, TextureView *depthAttachment) { + Pipeline *FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array &shaderBinaries) { auto it{map.find(packedState)}; if (it != map.end()) return it->second.get(); - return map.emplace(packedState, std::make_unique(ctx, textures, constantBuffers, packedState, shaderBinaries, colorAttachments, depthAttachment)).first->second.get(); + bundle.Reset(packedState); + auto accessor{RuntimeGraphicsPipelineStateAccessor{bundle, ctx, textures, constantBuffers, shaderBinaries}}; + return map.emplace(packedState, std::make_unique(ctx, accessor, packedState)).first->second.get(); } }; } diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp index a37f04c6..ff35d86d 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp @@ -432,7 +432,7 @@ namespace skyline::gpu::interconnect::maxwell3d { } } - auto newPipeline{pipelineManager.FindOrCreate(ctx, textures, constantBuffers, packedState, shaderBinaries, colorAttachments, depthAttachment)}; + auto newPipeline{pipelineManager.FindOrCreate(ctx, textures, constantBuffers, packedState, shaderBinaries)}; if (pipeline) pipeline->AddTransition(newPipeline); pipeline = newPipeline; diff --git a/app/src/main/cpp/skyline/gpu/shaders/helper_shaders.cpp b/app/src/main/cpp/skyline/gpu/shaders/helper_shaders.cpp index 275039e4..980831d9 100644 --- a/app/src/main/cpp/skyline/gpu/shaders/helper_shaders.cpp +++ b/app/src/main/cpp/skyline/gpu/shaders/helper_shaders.cpp @@ -136,6 +136,7 @@ namespace skyline::gpu { .scissorCount = 1 }; + std::array colorFormats{colorAttachment ? colorAttachment->format->vkFormat : vk::Format::eUndefined}; return gpu.graphicsPipelineCache.GetCompiledPipeline(cache::GraphicsPipelineCache::PipelineState{ .shaderStages = shaderStages, .vertexState = vertexState, @@ -147,8 +148,8 @@ namespace skyline::gpu { .depthStencilState = depthStencilState, .colorBlendState = blendState, .dynamicState = {}, - .colorAttachments = span{colorAttachment}, - .depthStencilAttachment = depthStencilAttachment, + .colorFormats = colorFormats, + .depthStencilFormat = depthStencilAttachment ? depthStencilAttachment->format->vkFormat : vk::Format::eUndefined, }, layoutBindings, pushConstantRanges, true); }