Skip to content

Commit e689cbf

Browse files
committed
vulkan: optimized dynamicStateBits
1 parent b25bc65 commit e689cbf

File tree

4 files changed

+179
-70
lines changed

4 files changed

+179
-70
lines changed

axmol/rhi/RenderContext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ class RenderContext : public ax::Object
261261
* Update both front and back stencil reference value.
262262
* @param value Specifies stencil reference value.
263263
*/
264-
void setStencilReferenceValue(uint32_t value);
264+
virtual void setStencilReferenceValue(uint32_t value);
265265

266266
protected:
267267
virtual ~RenderContext() = default;

axmol/rhi/vulkan/RenderContextVK.cpp

Lines changed: 135 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@
4242
namespace ax::rhi::vk
4343
{
4444

45-
static constexpr uint32_t MAX_DESCRIPTOR_SETS_PER_FRAME = 1024;
46-
4745
/*
4846
* Helper: map PrimitiveType to VkPrimitiveTopology
4947
*
@@ -96,6 +94,24 @@ static VkIndexType toVkIndexType(IndexFormat fmt)
9694
}
9795
}
9896

97+
inline bool nearlyEqual(float a, float b, float eps = 1e-6f)
98+
{
99+
return std::fabs(a - b) < eps;
100+
}
101+
102+
inline bool operator==(const VkViewport& a, const VkViewport& b)
103+
{
104+
return nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y) && nearlyEqual(a.width, b.width) &&
105+
nearlyEqual(a.height, b.height) && nearlyEqual(a.minDepth, b.minDepth) &&
106+
nearlyEqual(a.maxDepth, b.maxDepth);
107+
}
108+
109+
inline bool operator==(const VkRect2D& a, const VkRect2D& b)
110+
{
111+
return a.offset.x == b.offset.x && a.offset.y == b.offset.y && a.extent.width == b.extent.width &&
112+
a.extent.height == b.extent.height;
113+
}
114+
99115
// NOTE: This implementation assumes the existence of a Vulkan driver context that owns device, queues,
100116
// swapchain, render pass, and descriptor management. Adapt integration points to your driver as needed.
101117

@@ -323,6 +339,8 @@ void RenderContextImpl::createDescriptorPool()
323339
/*{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 32},*/ // SSBO, unused currently
324340
};
325341

342+
constexpr uint32_t MAX_DESCRIPTOR_SETS_PER_FRAME = 1024;
343+
326344
VkDescriptorPoolCreateInfo poolInfo{};
327345
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
328346
poolInfo.poolSizeCount = static_cast<uint32_t>(std::size(poolSizes));
@@ -603,8 +621,9 @@ void RenderContextImpl::endRenderPass()
603621
rtImpl->endRenderPass(_currentCmdBuffer);
604622

605623
// Reset state cache
606-
_programState = nullptr;
607-
_vertexLayout = nullptr;
624+
_programState = nullptr;
625+
_vertexLayout = nullptr;
626+
_boundPipeline = nullptr;
608627

609628
AX_SAFE_RELEASE_NULL(_indexBuffer);
610629
AX_SAFE_RELEASE_NULL(_vertexBuffer);
@@ -705,6 +724,17 @@ void RenderContextImpl::updatePipelineState(const RenderTarget* rt, const Pipeli
705724
AXASSERT(_renderPipeline, "RenderPipelineImpl not set");
706725
_renderPipeline->prepareUpdate(_depthStencilState);
707726
_renderPipeline->update(rt, desc);
727+
728+
// Bind pipeline
729+
auto pipeline = _renderPipeline->getVkPipeline();
730+
if (_boundPipeline != pipeline)
731+
{
732+
vkCmdBindPipeline(_currentCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, _renderPipeline->getVkPipeline());
733+
_boundPipeline = pipeline;
734+
735+
// Prime dynamic states required by this pipeline
736+
bitmask::set(_inFlightDynamicDirtyBits[_currentFrame], PIPELINE_REQUIRED_DYNAMIC_BITS);
737+
}
708738
}
709739

710740
void RenderContextImpl::setViewport(int x, int y, unsigned int w, unsigned int h)
@@ -720,7 +750,11 @@ void RenderContextImpl::setViewport(int x, int y, unsigned int w, unsigned int h
720750
vp.minDepth = 0.0f;
721751
vp.maxDepth = 1.0f;
722752

723-
_cachedViewport = vp;
753+
if (vp != _cachedViewport)
754+
{
755+
_cachedViewport = vp;
756+
markDynamicStateDirty(DynamicStateBits::Viewport);
757+
}
724758
}
725759

726760
void RenderContextImpl::setScissorRect(bool isEnabled, float x, float y, float width, float height)
@@ -748,37 +782,64 @@ void RenderContextImpl::setScissorRect(bool isEnabled, float x, float y, float w
748782
rect.extent = {_renderTargetWidth, _renderTargetHeight};
749783
}
750784

751-
_scissorEnabled = isEnabled;
752-
_cachedScissor = rect;
785+
if (_scissorEnabled != isEnabled || _cachedScissor != rect)
786+
{
787+
_scissorEnabled = isEnabled;
788+
_cachedScissor = rect;
789+
markDynamicStateDirty(DynamicStateBits::Scissor);
790+
}
753791
}
754792

755793
void RenderContextImpl::setCullMode(CullMode mode)
756794
{
795+
VkCullModeFlags nativeMode{0};
757796
switch (mode)
758797
{
759798
case CullMode::NONE:
760-
_cachedCullMode = VK_CULL_MODE_NONE;
799+
nativeMode = VK_CULL_MODE_NONE;
761800
break;
762801
case CullMode::BACK:
763-
_cachedCullMode = VK_CULL_MODE_BACK_BIT;
802+
nativeMode = VK_CULL_MODE_BACK_BIT;
764803
break;
765804
case CullMode::FRONT:
766-
_cachedCullMode = VK_CULL_MODE_FRONT_BIT;
805+
nativeMode = VK_CULL_MODE_FRONT_BIT;
767806
break;
768807
}
808+
809+
if (_cachedCullMode != nativeMode)
810+
{
811+
_cachedCullMode = nativeMode;
812+
markDynamicStateDirty(DynamicStateBits::CullMode);
813+
}
769814
}
770815

771816
void RenderContextImpl::setWinding(Winding winding)
772817
{
818+
VkFrontFace frontFace = VkFrontFace::VK_FRONT_FACE_MAX_ENUM;
773819
switch (winding)
774820
{
775821
case Winding::CLOCK_WISE:
776-
_cachedFrontFace = VK_FRONT_FACE_CLOCKWISE;
822+
frontFace = VK_FRONT_FACE_CLOCKWISE;
777823
break;
778824
case Winding::COUNTER_CLOCK_WISE:
779-
_cachedFrontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
825+
frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
780826
break;
781827
}
828+
829+
if (frontFace != _cachedFrontFace)
830+
{
831+
_cachedFrontFace = frontFace;
832+
markDynamicStateDirty(DynamicStateBits::FrontFace);
833+
}
834+
}
835+
836+
void RenderContextImpl::setStencilReferenceValue(uint32_t value)
837+
{
838+
if (value != _stencilReferenceValue)
839+
{
840+
RenderContext::setStencilReferenceValue(value);
841+
markDynamicStateDirty(DynamicStateBits::StencilRef);
842+
}
782843
}
783844

784845
void RenderContextImpl::setVertexBuffer(Buffer* buffer)
@@ -812,14 +873,12 @@ void RenderContextImpl::prepareDrawing()
812873
{
813874
AXASSERT(_programState, "ProgramState must be set before drawing");
814875
AXASSERT(_renderPipeline, "RenderPipelineImpl must be set before drawing");
876+
AXASSERT(_boundPipeline, "boundPipeline must be set before drawing");
815877

816878
// Populate CPU-side uniforms via callbacks
817879
for (auto& cb : _programState->getCallbackUniforms())
818880
cb.second(_programState, cb.first);
819881

820-
// Bind pipeline
821-
vkCmdBindPipeline(_currentCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, _renderPipeline->getVkPipeline());
822-
823882
// Acquire descriptor sets for this frame, matching current pipeline layout
824883
VkPipelineLayout pipelineLayout = _renderPipeline->getVkPipelineLayout();
825884
const auto dslState = _renderPipeline->getDescriptorSetLayoutState();
@@ -900,48 +959,42 @@ void RenderContextImpl::prepareDrawing()
900959
imageInfos.clear();
901960
imageInfos.reserve(dslState->samplerDescriptorCount);
902961

903-
const bool hasSamplerSet = (dslState->descriptorSetLayoutCount > RenderPipelineImpl::SET_INDEX_SAMPLER) &&
904-
(descriptorSets[RenderPipelineImpl::SET_INDEX_SAMPLER] != VK_NULL_HANDLE);
905-
906-
if (hasSamplerSet)
962+
for (const auto& [bindingIndex, bindingSet] : _programState->getTextureBindingSets())
907963
{
908-
for (const auto& [bindingIndex, bindingSet] : _programState->getTextureBindingSets())
909-
{
910-
const auto& texs = bindingSet.texs;
911-
if (texs.empty())
912-
continue;
964+
const auto& texs = bindingSet.texs;
965+
if (texs.empty())
966+
continue;
913967

914-
// Remember current offset in imageInfos before appending
915-
const size_t offset = imageInfos.size();
968+
// Remember current offset in imageInfos before appending
969+
const size_t offset = imageInfos.size();
916970

917-
if (texs.size() == 1)
971+
if (texs.size() == 1)
972+
{
973+
auto* textureImpl = static_cast<TextureImpl*>(texs[0]);
974+
VkDescriptorImageInfo& imageInfo = imageInfos.emplace_back();
975+
imageInfo.sampler = textureImpl->getSampler();
976+
imageInfo.imageView = textureImpl->internalHandle().view;
977+
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
978+
}
979+
else
980+
{
981+
for (auto* tex : texs)
918982
{
919-
auto* textureImpl = static_cast<TextureImpl*>(texs[0]);
983+
auto* textureImpl = static_cast<TextureImpl*>(tex);
920984
VkDescriptorImageInfo& imageInfo = imageInfos.emplace_back();
921985
imageInfo.sampler = textureImpl->getSampler();
922986
imageInfo.imageView = textureImpl->internalHandle().view;
923987
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
924988
}
925-
else
926-
{
927-
for (auto* tex : texs)
928-
{
929-
auto* textureImpl = static_cast<TextureImpl*>(tex);
930-
VkDescriptorImageInfo& imageInfo = imageInfos.emplace_back();
931-
imageInfo.sampler = textureImpl->getSampler();
932-
imageInfo.imageView = textureImpl->internalHandle().view;
933-
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
934-
}
935-
}
936-
937-
VkWriteDescriptorSet& write = writes.emplace_back();
938-
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
939-
write.dstSet = descriptorSets[RenderPipelineImpl::SET_INDEX_SAMPLER];
940-
write.dstBinding = bindingIndex;
941-
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
942-
write.descriptorCount = static_cast<uint32_t>(texs.size());
943-
write.pImageInfo = imageInfos.data() + offset;
944989
}
990+
991+
VkWriteDescriptorSet& write = writes.emplace_back();
992+
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
993+
write.dstSet = descriptorSets[RenderPipelineImpl::SET_INDEX_SAMPLER];
994+
write.dstBinding = bindingIndex;
995+
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
996+
write.descriptorCount = static_cast<uint32_t>(texs.size());
997+
write.pImageInfo = imageInfos.data() + offset;
945998
}
946999

9471000
// Commit descriptor writes
@@ -951,16 +1004,8 @@ void RenderContextImpl::prepareDrawing()
9511004
}
9521005

9531006
// Bind descriptor sets: bind only the sets that exist
954-
uint32_t setCountToBind = dslState->descriptorSetLayoutCount;
955-
if (!hasSamplerSet && setCountToBind > 1)
956-
setCountToBind = 1;
957-
958-
AXASSERT(!writes.empty(), "No descriptor writes prepared before vkUpdateDescriptorSets");
959-
AXASSERT(setCountToBind == dslState->descriptorSetLayoutCount || setCountToBind == 1,
960-
"Descriptor set count mismatch with pipeline layout");
961-
962-
vkCmdBindDescriptorSets(_currentCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, setCountToBind,
963-
descriptorSets.data(), 0, nullptr);
1007+
vkCmdBindDescriptorSets(_currentCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0,
1008+
dslState->descriptorSetLayoutCount, descriptorSets.data(), 0, nullptr);
9641009

9651010
// Bind vertex buffers
9661011
if (!_instanceBuffer)
@@ -976,20 +1021,43 @@ void RenderContextImpl::prepareDrawing()
9761021
vkCmdBindVertexBuffers(_currentCmdBuffer, 0, 2, buffers, offsets);
9771022
}
9781023

979-
// Dynamic states
980-
vkCmdSetViewport(_currentCmdBuffer, 0, 1, &_cachedViewport);
1024+
// Apply dynamic states based on dirty bits
1025+
auto& dynamicDirtyBits = _inFlightDynamicDirtyBits[_currentFrame];
1026+
if (bitmask::any(dynamicDirtyBits, DynamicStateBits::Viewport))
1027+
{
1028+
vkCmdSetViewport(_currentCmdBuffer, 0, 1, &_cachedViewport);
1029+
bitmask::clear(dynamicDirtyBits, DynamicStateBits::Viewport);
1030+
}
9811031

982-
if (_scissorEnabled)
983-
vkCmdSetScissor(_currentCmdBuffer, 0, 1, &_cachedScissor);
984-
else
1032+
if (bitmask::any(dynamicDirtyBits, DynamicStateBits::Scissor))
1033+
{
1034+
if (_scissorEnabled)
1035+
vkCmdSetScissor(_currentCmdBuffer, 0, 1, &_cachedScissor);
1036+
else
1037+
{
1038+
VkRect2D fullRect{{0, 0}, {_renderTargetWidth, _renderTargetHeight}};
1039+
vkCmdSetScissor(_currentCmdBuffer, 0, 1, &fullRect);
1040+
}
1041+
bitmask::clear(dynamicDirtyBits, DynamicStateBits::Scissor);
1042+
}
1043+
1044+
if (bitmask::any(dynamicDirtyBits, DynamicStateBits::StencilRef))
9851045
{
986-
VkRect2D fullRect{{0, 0}, {_renderTargetWidth, _renderTargetHeight}};
987-
vkCmdSetScissor(_currentCmdBuffer, 0, 1, &fullRect);
1046+
vkCmdSetStencilReference(_currentCmdBuffer, VK_STENCIL_FACE_FRONT_AND_BACK, _stencilReferenceValue);
1047+
bitmask::clear(dynamicDirtyBits, DynamicStateBits::StencilRef);
9881048
}
9891049

990-
vkCmdSetStencilReference(_currentCmdBuffer, VK_STENCIL_FACE_FRONT_AND_BACK, _stencilReferenceValue);
991-
vkCmdSetCullModeEXT(_currentCmdBuffer, _cachedCullMode);
992-
vkCmdSetFrontFaceEXT(_currentCmdBuffer, _cachedFrontFace);
1050+
if (bitmask::any(dynamicDirtyBits, DynamicStateBits::CullMode))
1051+
{
1052+
vkCmdSetCullModeEXT(_currentCmdBuffer, _cachedCullMode);
1053+
bitmask::clear(dynamicDirtyBits, DynamicStateBits::CullMode);
1054+
}
1055+
1056+
if (bitmask::any(dynamicDirtyBits, DynamicStateBits::FrontFace))
1057+
{
1058+
vkCmdSetFrontFaceEXT(_currentCmdBuffer, _cachedFrontFace);
1059+
bitmask::clear(dynamicDirtyBits, DynamicStateBits::FrontFace);
1060+
}
9931061
}
9941062

9951063
void RenderContextImpl::drawArrays(PrimitiveType primitiveType,

0 commit comments

Comments
 (0)