From e71ca05f195f9317c31b0974d5d3a3553992746a Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Wed, 14 Sep 2022 21:53:16 +0100 Subject: [PATCH] Avoid bitfields for signed enum types in PackedPipelineState --- .../maxwell_3d/packed_pipeline_state.cpp | 282 ++++++++++-------- .../maxwell_3d/packed_pipeline_state.h | 38 ++- .../maxwell_3d/pipeline_manager.cpp | 8 +- 3 files changed, 184 insertions(+), 144 deletions(-) diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.cpp index ad8d3805..9ebfffb6 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.cpp @@ -19,7 +19,7 @@ namespace skyline::gpu::interconnect::maxwell3d { void PackedPipelineState::SetVertexBinding(u32 index, engine::VertexStream stream, engine::VertexStreamInstance instance) { vertexBindings[index].stride = stream.format.stride; - vertexBindings[index].inputRate = instance.isInstanced ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex; + vertexBindings[index].inputRate = static_cast(instance.isInstanced ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex); vertexBindings[index].enable = stream.format.enable; vertexBindings[index].divisor = stream.frequency; } @@ -33,16 +33,23 @@ namespace skyline::gpu::interconnect::maxwell3d { void PackedPipelineState::SetPolygonMode(engine::PolygonMode mode) { switch (mode) { case engine::PolygonMode::Fill: - polygonMode = vk::PolygonMode::eFill; + polygonMode = static_cast(vk::PolygonMode::eFill); + break; case engine::PolygonMode::Line: - polygonMode = vk::PolygonMode::eLine; + polygonMode = static_cast(vk::PolygonMode::eLine); + break; case engine::PolygonMode::Point: - polygonMode = vk::PolygonMode::ePoint; + polygonMode = static_cast(vk::PolygonMode::ePoint); + break; default: throw exception("Invalid polygon mode: 0x{:X}", static_cast(mode)); } } + vk::PolygonMode PackedPipelineState::GetPolygonMode() const { + return static_cast(polygonMode); + } + void PackedPipelineState::SetCullMode(bool enable, engine::CullFace mode) { if (!enable) { cullMode = {}; @@ -52,67 +59,82 @@ namespace skyline::gpu::interconnect::maxwell3d { switch (mode) { case engine::CullFace::Front: cullMode = VK_CULL_MODE_FRONT_BIT; + break; case engine::CullFace::Back: cullMode = VK_CULL_MODE_BACK_BIT; + break; case engine::CullFace::FrontAndBack: cullMode = VK_CULL_MODE_FRONT_BIT | VK_CULL_MODE_BACK_BIT; + break; default: throw exception("Invalid cull mode: 0x{:X}", static_cast(mode)); } } - static vk::CompareOp ConvertCompareFunc(engine::CompareFunc func) { + static u8 ConvertCompareFunc(engine::CompareFunc func) { if (func < engine::CompareFunc::D3DNever || func > engine::CompareFunc::OglAlways || (func > engine::CompareFunc::D3DAlways && func < engine::CompareFunc::OglNever)) throw exception("Invalid comparision function: 0x{:X}", static_cast(func)); u32 val{static_cast(func)}; // VK CompareOp values match 1:1 with Maxwell with some small maths - return static_cast(func >= engine::CompareFunc::OglNever ? val - 0x200 : val - 1); + return static_cast(func >= engine::CompareFunc::OglNever ? val - 0x200 : val - 1); } void PackedPipelineState::SetDepthFunc(engine::CompareFunc func) { depthFunc = ConvertCompareFunc(func); } + vk::CompareOp PackedPipelineState::GetDepthFunc() const { + return static_cast(depthFunc); + } + void PackedPipelineState::SetLogicOp(engine::LogicOp::Func op) { if (op < engine::LogicOp::Func::Clear || op > engine::LogicOp::Func::Set) throw exception("Invalid logical operation: 0x{:X}", static_cast(op)); // VK LogicOp values match 1:1 with Maxwell - logicOp = static_cast(static_cast(op) - static_cast(engine::LogicOp::Func::Clear)); + logicOp = static_cast(static_cast(op) - static_cast(engine::LogicOp::Func::Clear)); } - static vk::StencilOp ConvertStencilOp(engine::StencilOps::Op op) { - switch (op) { - case engine::StencilOps::Op::OglZero: - case engine::StencilOps::Op::D3DZero: - return vk::StencilOp::eZero; - case engine::StencilOps::Op::D3DKeep: - case engine::StencilOps::Op::OglKeep: - return vk::StencilOp::eKeep; - case engine::StencilOps::Op::D3DReplace: - case engine::StencilOps::Op::OglReplace: - return vk::StencilOp::eReplace; - case engine::StencilOps::Op::D3DIncrSat: - case engine::StencilOps::Op::OglIncrSat: - return vk::StencilOp::eIncrementAndClamp; - case engine::StencilOps::Op::D3DDecrSat: - case engine::StencilOps::Op::OglDecrSat: - return vk::StencilOp::eDecrementAndClamp; - case engine::StencilOps::Op::D3DInvert: - case engine::StencilOps::Op::OglInvert: - return vk::StencilOp::eInvert; - case engine::StencilOps::Op::D3DIncr: - case engine::StencilOps::Op::OglIncr: - return vk::StencilOp::eIncrementAndWrap; - case engine::StencilOps::Op::D3DDecr: - case engine::StencilOps::Op::OglDecr: - return vk::StencilOp::eDecrementAndWrap; - default: - throw exception("Invalid stencil operation: 0x{:X}", static_cast(op)); - } + vk::LogicOp PackedPipelineState::GetLogicOp() const { + return static_cast(logicOp); + } + + static u8 ConvertStencilOp(engine::StencilOps::Op op) { + auto conv{[&]() { + switch (op) { + case engine::StencilOps::Op::OglZero: + case engine::StencilOps::Op::D3DZero: + return vk::StencilOp::eZero; + case engine::StencilOps::Op::D3DKeep: + case engine::StencilOps::Op::OglKeep: + return vk::StencilOp::eKeep; + case engine::StencilOps::Op::D3DReplace: + case engine::StencilOps::Op::OglReplace: + return vk::StencilOp::eReplace; + case engine::StencilOps::Op::D3DIncrSat: + case engine::StencilOps::Op::OglIncrSat: + return vk::StencilOp::eIncrementAndClamp; + case engine::StencilOps::Op::D3DDecrSat: + case engine::StencilOps::Op::OglDecrSat: + return vk::StencilOp::eDecrementAndClamp; + case engine::StencilOps::Op::D3DInvert: + case engine::StencilOps::Op::OglInvert: + return vk::StencilOp::eInvert; + case engine::StencilOps::Op::D3DIncr: + case engine::StencilOps::Op::OglIncr: + return vk::StencilOp::eIncrementAndWrap; + case engine::StencilOps::Op::D3DDecr: + case engine::StencilOps::Op::OglDecr: + return vk::StencilOp::eDecrementAndWrap; + default: + throw exception("Invalid stencil operation: 0x{:X}", static_cast(op)); + } + }}; + + return static_cast(conv()); } static PackedPipelineState::StencilOps PackStencilOps(engine::StencilOps ops) { @@ -136,88 +158,96 @@ namespace skyline::gpu::interconnect::maxwell3d { (write.aEnable ? VK_COLOR_COMPONENT_A_BIT : 0); }; - static vk::BlendOp ConvertBlendOp(engine::BlendOp op) { - switch (op) { - case engine::BlendOp::D3DAdd: - case engine::BlendOp::OglFuncAdd: - return vk::BlendOp::eAdd; - case engine::BlendOp::D3DSubtract: - case engine::BlendOp::OglFuncSubtract: - return vk::BlendOp::eSubtract; - case engine::BlendOp::D3DRevSubtract: - case engine::BlendOp::OglFuncReverseSubtract: - return vk::BlendOp::eReverseSubtract; - case engine::BlendOp::D3DMin: - case engine::BlendOp::OglMin: - return vk::BlendOp::eMin; - case engine::BlendOp::D3DMax: - case engine::BlendOp::OglMax: - return vk::BlendOp::eMax; - default: - throw exception("Invalid blend operation: 0x{:X}", static_cast(op)); - } + static u8 ConvertBlendOp(engine::BlendOp op) { + auto conv{[&]() { + switch (op) { + case engine::BlendOp::D3DAdd: + case engine::BlendOp::OglFuncAdd: + return vk::BlendOp::eAdd; + case engine::BlendOp::D3DSubtract: + case engine::BlendOp::OglFuncSubtract: + return vk::BlendOp::eSubtract; + case engine::BlendOp::D3DRevSubtract: + case engine::BlendOp::OglFuncReverseSubtract: + return vk::BlendOp::eReverseSubtract; + case engine::BlendOp::D3DMin: + case engine::BlendOp::OglMin: + return vk::BlendOp::eMin; + case engine::BlendOp::D3DMax: + case engine::BlendOp::OglMax: + return vk::BlendOp::eMax; + default: + throw exception("Invalid blend operation: 0x{:X}", static_cast(op)); + } + }}; + + return static_cast(conv()); } - static vk::BlendFactor ConvertBlendFactor(engine::BlendCoeff coeff) { - switch (coeff) { - case engine::BlendCoeff::OglZero: - case engine::BlendCoeff::D3DZero: - return vk::BlendFactor::eZero; - case engine::BlendCoeff::OglOne: - case engine::BlendCoeff::D3DOne: - return vk::BlendFactor::eOne; - case engine::BlendCoeff::OglSrcColor: - case engine::BlendCoeff::D3DSrcColor: - return vk::BlendFactor::eSrcColor; - case engine::BlendCoeff::OglOneMinusSrcColor: - case engine::BlendCoeff::D3DInvSrcColor: - return vk::BlendFactor::eOneMinusSrcColor; - case engine::BlendCoeff::OglSrcAlpha: - case engine::BlendCoeff::D3DSrcAlpha: - return vk::BlendFactor::eSrcAlpha; - case engine::BlendCoeff::OglOneMinusSrcAlpha: - case engine::BlendCoeff::D3DInvSrcAlpha: - return vk::BlendFactor::eOneMinusSrcAlpha; - case engine::BlendCoeff::OglDstAlpha: - case engine::BlendCoeff::D3DDstAlpha: - return vk::BlendFactor::eDstAlpha; - case engine::BlendCoeff::OglOneMinusDstAlpha: - case engine::BlendCoeff::D3DInvDstAlpha: - return vk::BlendFactor::eOneMinusDstAlpha; - case engine::BlendCoeff::OglDstColor: - case engine::BlendCoeff::D3DDstColor: - return vk::BlendFactor::eDstColor; - case engine::BlendCoeff::OglOneMinusDstColor: - case engine::BlendCoeff::D3DInvDstColor: - return vk::BlendFactor::eOneMinusDstColor; - case engine::BlendCoeff::OglSrcAlphaSaturate: - case engine::BlendCoeff::D3DSrcAlphaSaturate: - return vk::BlendFactor::eSrcAlphaSaturate; - case engine::BlendCoeff::OglConstantColor: - case engine::BlendCoeff::D3DBlendCoeff: - return vk::BlendFactor::eConstantColor; - case engine::BlendCoeff::OglOneMinusConstantColor: - case engine::BlendCoeff::D3DInvBlendCoeff: - return vk::BlendFactor::eOneMinusConstantColor; - case engine::BlendCoeff::OglConstantAlpha: - return vk::BlendFactor::eConstantAlpha; - case engine::BlendCoeff::OglOneMinusConstantAlpha: - return vk::BlendFactor::eOneMinusConstantAlpha; - case engine::BlendCoeff::OglSrc1Color: - case engine::BlendCoeff::D3DSrc1Color: - return vk::BlendFactor::eSrc1Color; - case engine::BlendCoeff::OglInvSrc1Color: - case engine::BlendCoeff::D3DInvSrc1Color: - return vk::BlendFactor::eOneMinusSrc1Color; - case engine::BlendCoeff::OglSrc1Alpha: - case engine::BlendCoeff::D3DSrc1Alpha: - return vk::BlendFactor::eSrc1Alpha; - case engine::BlendCoeff::OglInvSrc1Alpha: - case engine::BlendCoeff::D3DInvSrc1Alpha: - return vk::BlendFactor::eOneMinusSrc1Alpha; - default: - throw exception("Invalid blend coefficient type: 0x{:X}", static_cast(coeff)); - } + static u8 ConvertBlendFactor(engine::BlendCoeff coeff) { + auto conv{[&]() { + switch (coeff) { + case engine::BlendCoeff::OglZero: + case engine::BlendCoeff::D3DZero: + return vk::BlendFactor::eZero; + case engine::BlendCoeff::OglOne: + case engine::BlendCoeff::D3DOne: + return vk::BlendFactor::eOne; + case engine::BlendCoeff::OglSrcColor: + case engine::BlendCoeff::D3DSrcColor: + return vk::BlendFactor::eSrcColor; + case engine::BlendCoeff::OglOneMinusSrcColor: + case engine::BlendCoeff::D3DInvSrcColor: + return vk::BlendFactor::eOneMinusSrcColor; + case engine::BlendCoeff::OglSrcAlpha: + case engine::BlendCoeff::D3DSrcAlpha: + return vk::BlendFactor::eSrcAlpha; + case engine::BlendCoeff::OglOneMinusSrcAlpha: + case engine::BlendCoeff::D3DInvSrcAlpha: + return vk::BlendFactor::eOneMinusSrcAlpha; + case engine::BlendCoeff::OglDstAlpha: + case engine::BlendCoeff::D3DDstAlpha: + return vk::BlendFactor::eDstAlpha; + case engine::BlendCoeff::OglOneMinusDstAlpha: + case engine::BlendCoeff::D3DInvDstAlpha: + return vk::BlendFactor::eOneMinusDstAlpha; + case engine::BlendCoeff::OglDstColor: + case engine::BlendCoeff::D3DDstColor: + return vk::BlendFactor::eDstColor; + case engine::BlendCoeff::OglOneMinusDstColor: + case engine::BlendCoeff::D3DInvDstColor: + return vk::BlendFactor::eOneMinusDstColor; + case engine::BlendCoeff::OglSrcAlphaSaturate: + case engine::BlendCoeff::D3DSrcAlphaSaturate: + return vk::BlendFactor::eSrcAlphaSaturate; + case engine::BlendCoeff::OglConstantColor: + case engine::BlendCoeff::D3DBlendCoeff: + return vk::BlendFactor::eConstantColor; + case engine::BlendCoeff::OglOneMinusConstantColor: + case engine::BlendCoeff::D3DInvBlendCoeff: + return vk::BlendFactor::eOneMinusConstantColor; + case engine::BlendCoeff::OglConstantAlpha: + return vk::BlendFactor::eConstantAlpha; + case engine::BlendCoeff::OglOneMinusConstantAlpha: + return vk::BlendFactor::eOneMinusConstantAlpha; + case engine::BlendCoeff::OglSrc1Color: + case engine::BlendCoeff::D3DSrc1Color: + return vk::BlendFactor::eSrc1Color; + case engine::BlendCoeff::OglInvSrc1Color: + case engine::BlendCoeff::D3DInvSrc1Color: + return vk::BlendFactor::eOneMinusSrc1Color; + case engine::BlendCoeff::OglSrc1Alpha: + case engine::BlendCoeff::D3DSrc1Alpha: + return vk::BlendFactor::eSrc1Alpha; + case engine::BlendCoeff::OglInvSrc1Alpha: + case engine::BlendCoeff::D3DInvSrc1Alpha: + return vk::BlendFactor::eOneMinusSrc1Alpha; + default: + throw exception("Invalid blend coefficient type: 0x{:X}", static_cast(coeff)); + } + }}; + + return static_cast(conv()); } static PackedPipelineState::AttachmentBlendState PackAttachmentBlendState(bool enable, engine::CtWrite writeMask, auto blend) { @@ -244,10 +274,10 @@ namespace skyline::gpu::interconnect::maxwell3d { std::array PackedPipelineState::GetStencilOpsState() const { auto convertFaceOps{[](StencilOps ops) { return vk::StencilOpState{ - .failOp = ops.fail, - .passOp = ops.zPass, - .depthFailOp = ops.zFail, - .compareOp = ops.func, + .failOp = static_cast(ops.fail), + .passOp = static_cast(ops.zPass), + .depthFailOp = static_cast(ops.zFail), + .compareOp = static_cast(ops.func), }; }}; @@ -262,12 +292,12 @@ namespace skyline::gpu::interconnect::maxwell3d { return { .colorWriteMask = vk::ColorComponentFlags{state.colorWriteMask}, - .colorBlendOp = state.colorBlendOp, - .srcColorBlendFactor = state.srcColorBlendFactor, - .dstColorBlendFactor = state.dstColorBlendFactor, - .alphaBlendOp = state.alphaBlendOp, - .srcAlphaBlendFactor = state.srcAlphaBlendFactor, - .dstAlphaBlendFactor = state.dstAlphaBlendFactor, + .colorBlendOp = static_cast(state.colorBlendOp), + .srcColorBlendFactor = static_cast(state.srcColorBlendFactor), + .dstColorBlendFactor = static_cast(state.dstColorBlendFactor), + .alphaBlendOp = static_cast(state.alphaBlendOp), + .srcAlphaBlendFactor = static_cast(state.srcAlphaBlendFactor), + .dstAlphaBlendFactor = static_cast(state.dstAlphaBlendFactor), .blendEnable = state.blendEnable }; } diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.h b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.h index 6a0ce1f3..76cb08f6 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.h @@ -19,10 +19,10 @@ namespace skyline::gpu::interconnect::maxwell3d { std::array shaderHashes; struct StencilOps { - vk::StencilOp zPass : 3; - vk::StencilOp fail : 3; - vk::StencilOp zFail : 3; - vk::CompareOp func : 3; + u8 zPass : 3; + u8 fail : 3; + u8 zFail : 3; + u8 func : 3; // 4 bits left for each stencil side }; @@ -37,7 +37,7 @@ namespace skyline::gpu::interconnect::maxwell3d { engine::TessellationParameters::Spacing spacing : 2; //!< Use SetTessellationParameters engine::TessellationParameters::OutputPrimitives outputPrimitives : 2; //!< Use SetTessellationParameters bool rasterizerDiscardEnable : 1; - vk::PolygonMode polygonMode : 2; //!< Use SetPolygonMode + u8 polygonMode : 2; //!< Use {Set,Get}PolygonMode VkCullModeFlags cullMode : 2; //!< Use SetCullMode bool flipYEnable : 1; bool frontFaceClockwise : 1; //!< With Y flip transformation already applied @@ -45,11 +45,11 @@ namespace skyline::gpu::interconnect::maxwell3d { engine::ProvokingVertex::Value provokingVertex : 1; bool depthTestEnable : 1; bool depthWriteEnable : 1; - vk::CompareOp depthFunc : 3; //!< Use SetDepthFunc + u8 depthFunc : 3; //!< Use {Set,Get}DepthFunc bool depthBoundsTestEnable : 1; bool stencilTestEnable : 1; bool logicOpEnable : 1; - vk::LogicOp logicOp : 4; //!< Use SetLogicOp + u8 logicOp : 4; //!< Use {Set,Get}LogicOp u8 bindlessTextureConstantBufferSlotSelect : 5; bool apiMandatedEarlyZ : 1; bool openGlNdc : 1; @@ -63,22 +63,26 @@ namespace skyline::gpu::interconnect::maxwell3d { struct VertexBinding { u16 stride : 12; - vk::VertexInputRate inputRate : 1; + u8 inputRate : 1; bool enable : 1; u8 _pad_ : 2; u32 divisor; + + vk::VertexInputRate GetInputRate() const { + return static_cast(inputRate); + } }; std::array vertexBindings; //!< Use {Set, Get}VertexBinding struct AttachmentBlendState { VkColorComponentFlags colorWriteMask : 4; - vk::BlendOp colorBlendOp : 3; - vk::BlendFactor srcColorBlendFactor : 5; - vk::BlendFactor dstColorBlendFactor : 5; - vk::BlendOp alphaBlendOp : 3; - vk::BlendFactor srcAlphaBlendFactor : 5; - vk::BlendFactor dstAlphaBlendFactor : 5; + u8 colorBlendOp : 3; + u8 srcColorBlendFactor : 5; + u8 dstColorBlendFactor : 5; + u8 alphaBlendOp : 3; + u8 srcAlphaBlendFactor : 5; + u8 dstAlphaBlendFactor : 5; bool blendEnable : 1; }; @@ -94,14 +98,20 @@ namespace skyline::gpu::interconnect::maxwell3d { void SetPolygonMode(engine::PolygonMode mode); + vk::PolygonMode GetPolygonMode() const; + void SetCullMode(bool enable, engine::CullFace mode); void SetDepthFunc(engine::CompareFunc func); + vk::CompareOp GetDepthFunc() const; + void SetStencilOps(engine::StencilOps front, engine::StencilOps back); void SetLogicOp(engine::LogicOp::Func op); + vk::LogicOp GetLogicOp() const; + void SetAttachmentBlendState(u32 index, bool enable, engine::CtWrite writeMask, engine::Blend blend); void SetAttachmentBlendState(u32 index, bool enable, engine::CtWrite writeMask, engine::BlendPerTarget blend); 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 818f644a..f412b135 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 @@ -374,7 +374,7 @@ namespace skyline::gpu::interconnect::maxwell3d { .inputRate = binding.divisor ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex, }); - if (binding.inputRate == vk::VertexInputRate::eInstance) { + if (binding.GetInputRate() == vk::VertexInputRate::eInstance) { if (!ctx.gpu.traits.supportsVertexAttributeDivisor) [[unlikely]] Logger::Warn("Vertex attribute divisor used on guest without host support"); else if (!ctx.gpu.traits.supportsVertexAttributeZeroDivisor && binding.divisor == 0) [[unlikely]] @@ -427,7 +427,7 @@ namespace skyline::gpu::interconnect::maxwell3d { auto &rasterizationCreateInfo{rasterizationState.get()}; rasterizationCreateInfo.rasterizerDiscardEnable = packedState.rasterizerDiscardEnable; - rasterizationCreateInfo.polygonMode = packedState.polygonMode; + rasterizationCreateInfo.polygonMode = packedState.GetPolygonMode(); rasterizationCreateInfo.cullMode = vk::CullModeFlags{packedState.cullMode}; rasterizationCreateInfo.frontFace = packedState.frontFaceClockwise ? vk::FrontFace::eClockwise : vk::FrontFace::eCounterClockwise; rasterizationCreateInfo.depthBiasEnable = packedState.depthBiasEnable; @@ -440,7 +440,7 @@ namespace skyline::gpu::interconnect::maxwell3d { vk::PipelineDepthStencilStateCreateInfo depthStencilState{ .depthTestEnable = packedState.depthTestEnable, .depthWriteEnable = packedState.depthWriteEnable, - .depthCompareOp = packedState.depthFunc, + .depthCompareOp = packedState.GetDepthFunc(), .depthBoundsTestEnable = packedState.depthBoundsTestEnable, .stencilTestEnable = packedState.stencilTestEnable }; @@ -453,7 +453,7 @@ namespace skyline::gpu::interconnect::maxwell3d { vk::PipelineColorBlendStateCreateInfo colorBlendState{ .logicOpEnable = packedState.logicOpEnable, - .logicOp = packedState.logicOp, + .logicOp = packedState.GetLogicOp(), .attachmentCount = static_cast(attachmentBlendStates.size()), .pAttachments = attachmentBlendStates.data() };