// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE #include #include #include #include #include #include #include #include #include #include #include #include namespace anki { void CommandBufferImpl::init(const CommandBufferInitInfo& init) { auto& pool = getManager().getAllocator().getMemoryPool(); m_alloc = CommandBufferAllocator(pool.getAllocationCallback(), pool.getAllocationCallbackUserData(), init.m_hints.m_chunkSize, 1.0, 0, false); m_flags = init.m_flags; #if ANKI_EXTRA_CHECKS m_state.m_secondLevel = !!(init.m_flags & CommandBufferFlag::kSecondLevel); #endif if(!!(init.m_flags & CommandBufferFlag::kSecondLevel)) { // TODO Need to hold a ref m_state.m_fb = static_cast(init.m_framebuffer.get()); } } void CommandBufferImpl::destroy() { ANKI_TRACE_SCOPED_EVENT(GL_CMD_BUFFER_DESTROY); #if ANKI_EXTRA_CHECKS if(!m_executed && m_firstCommand) { ANKI_GL_LOGW("Chain contains commands but never executed. This should only happen on exceptions"); } #endif GlCommand* command = m_firstCommand; while(command != nullptr) { GlCommand* next = command->m_nextCommand; // Get next before deleting m_alloc.deleteInstance(command); command = next; } ANKI_ASSERT(m_alloc.getMemoryPool().getUsersCount() == 1 && "Someone is holding a reference to the command buffer's allocator"); m_alloc = CommandBufferAllocator(); } Error CommandBufferImpl::executeAllCommands() { ANKI_ASSERT(m_firstCommand != nullptr && "Empty command buffer"); ANKI_ASSERT(m_lastCommand != nullptr && "Empty command buffer"); #if ANKI_EXTRA_CHECKS m_executed = true; #endif Error err = Error::kNone; GlState& state = static_cast(getManager()).getState(); GlCommand* command = m_firstCommand; while(command != nullptr && !err) { err = (*command)(state); ANKI_CHECK_GL_ERROR(); command = command->m_nextCommand; } return err; } void CommandBufferImpl::flushDrawcall(CommandBuffer& cmdb) { ANKI_ASSERT(!!(m_flags & CommandBufferFlag::GRAPHICS_WORK)); // // Set default state // if(m_state.m_mayContainUnsetState) [[unlikely]] { m_state.m_mayContainUnsetState = false; if(m_state.m_primitiveRestart == 2) { cmdb.setPrimitiveRestart(false); } if(m_state.m_fillMode == FillMode::COUNT) { cmdb.setFillMode(FillMode::SOLID); } if(m_state.m_cullMode == static_cast(0)) { cmdb.setCullMode(FaceSelectionBit::BACK); } if(m_state.m_polyOffsetFactor == -1.0) { cmdb.setPolygonOffset(0.0, 0.0); } for(U i = 0; i < 2; ++i) { FaceSelectionBit face = (i == 0) ? FaceSelectionBit::FRONT : FaceSelectionBit::BACK; if(m_state.m_stencilFail[i] == StencilOperation::COUNT) { cmdb.setStencilOperations(face, StencilOperation::KEEP, StencilOperation::KEEP, StencilOperation::KEEP); } if(m_state.m_stencilCompare[i] == CompareOperation::COUNT) { cmdb.setStencilCompareOperation(face, CompareOperation::ALWAYS); } if(m_state.m_stencilCompareMask[i] == StateTracker::DUMMY_STENCIL_MASK) { cmdb.setStencilCompareMask(face, kMaxU32); } if(m_state.m_stencilWriteMask[i] == StateTracker::DUMMY_STENCIL_MASK) { cmdb.setStencilWriteMask(face, kMaxU32); } if(m_state.m_stencilRef[i] == StateTracker::DUMMY_STENCIL_MASK) { cmdb.setStencilReference(face, 0); } } if(m_state.m_depthWrite == 2) { cmdb.setDepthWrite(true); } if(m_state.m_depthOp == CompareOperation::COUNT) { cmdb.setDepthCompareOperation(CompareOperation::LESS); } for(U i = 0; i < kMaxColorRenderTargets; ++i) { const auto& att = m_state.m_colorAtt[i]; if(att.m_writeMask == StateTracker::INVALID_COLOR_MASK) { cmdb.setColorChannelWriteMask(i, ColorBit::ALL); } if(att.m_blendSrcFactorRgb == BlendFactor::COUNT) { cmdb.setBlendFactors(i, BlendFactor::ONE, BlendFactor::ZERO, BlendFactor::ONE, BlendFactor::ZERO); } if(att.m_blendOpRgb == BlendOperation::COUNT) { cmdb.setBlendOperation(i, BlendOperation::ADD, BlendOperation::ADD); } } if(!m_state.m_scissorSet) { cmdb.setScissor(0, 0, kMaxU32, kMaxU32); } } // // Fire commands to change some state // class StencilCmd final : public GlCommand { public: GLenum m_face; GLenum m_func; GLint m_ref; GLuint m_compareMask; StencilCmd(GLenum face, GLenum func, GLint ref, GLuint mask) : m_face(face) , m_func(func) , m_ref(ref) , m_compareMask(mask) { } Error operator()(GlState&) { glStencilFuncSeparate(m_face, m_func, m_ref, m_compareMask); return Error::kNone; } }; for(U i = 0; i < 2; ++i) { if(m_state.m_glStencilFuncSeparateDirty[i]) { pushBackNewCommand(GL_FRONT + i, convertCompareOperation(m_state.m_stencilCompare[i]), m_state.m_stencilRef[i], m_state.m_stencilCompareMask[i]); m_state.m_glStencilFuncSeparateDirty[i] = false; } } class DepthTestCmd final : public GlCommand { public: Bool m_enable; DepthTestCmd(Bool enable) : m_enable(enable) { } Error operator()(GlState&) { if(m_enable) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } return Error::kNone; } }; if(m_state.maybeEnableDepthTest()) { pushBackNewCommand(m_state.m_depthTestEnabled); } class StencilTestCmd final : public GlCommand { public: Bool m_enable; StencilTestCmd(Bool enable) : m_enable(enable) { } Error operator()(GlState&) { if(m_enable) { glEnable(GL_STENCIL_TEST); } else { glDisable(GL_STENCIL_TEST); } return Error::kNone; } }; if(m_state.maybeEnableStencilTest()) { pushBackNewCommand(m_state.m_stencilTestEnabled); } class BlendCmd final : public GlCommand { public: U8 m_enableMask; U8 m_disableMask; BlendCmd(U8 enableMask, U8 disableMask) : m_enableMask(enableMask) , m_disableMask(disableMask) { } Error operator()(GlState&) { for(U i = 0; i < kMaxColorRenderTargets; ++i) { if(m_enableMask & (1 << i)) { glEnablei(GL_BLEND, i); } else if(m_disableMask & (1 << i)) { glDisablei(GL_BLEND, i); } } return Error::kNone; } }; U8 blendEnableMask = 0; U8 blendDisableMask = 0; for(U i = 0; i < kMaxColorRenderTargets; ++i) { if(m_state.maybeEnableBlend(i)) { if(m_state.m_colorAtt[i].m_enableBlend) { blendEnableMask |= 1 << i; } else { blendDisableMask |= 1 << i; } } } if(blendEnableMask || blendDisableMask) { pushBackNewCommand(blendEnableMask, blendDisableMask); } } } // end namespace anki