Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APPLE: ICB for multidrawindirect on macOS #1945

Merged
merged 2 commits into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pxr/imaging/hdSt/commandBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ HdStCommandBuffer::PrepareDraw(
for (auto const& batch : _drawBatches) {
batch->PrepareDraw(gfxCmds, renderPassState, resourceRegistry);
}

//
// Compute work that was set up for indirect command buffers and frustum
// culling in the batch preparation is submitted to device.
//
resourceRegistry->SubmitComputeWork();
}

void
Expand All @@ -102,13 +108,14 @@ HdStCommandBuffer::ExecuteDraw(
// Reset per-commandBuffer performance counters, updated by batch execution
HD_PERF_COUNTER_SET(HdPerfTokens->drawCalls, 0);
HD_PERF_COUNTER_SET(HdTokens->itemsDrawn, 0);

//
// draw batches
//
for (auto const& batch : _drawBatches) {
batch->ExecuteDraw(gfxCmds, renderPassState, resourceRegistry);
}

HD_PERF_COUNTER_SET(HdPerfTokens->drawBatches, _drawBatches.size());
}

Expand Down
188 changes: 143 additions & 45 deletions pxr/imaging/hdSt/pipelineDrawBatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "pxr/imaging/hgi/blitCmds.h"
#include "pxr/imaging/hgi/blitCmdsOps.h"
#include "pxr/imaging/hgi/graphicsPipeline.h"
#include "pxr/imaging/hgi/indirectCommandEncoder.h"
#include "pxr/imaging/hgi/resourceBindings.h"

#include "pxr/base/tf/diagnostic.h"
Expand Down Expand Up @@ -931,7 +932,7 @@ HdSt_PipelineDrawBatch::_HasNothingToDraw() const

void
HdSt_PipelineDrawBatch::PrepareDraw(
HgiGraphicsCmds *,
HgiGraphicsCmds *gfxCmds,
HdStRenderPassStateSharedPtr const & renderPassState,
HdStResourceRegistrySharedPtr const & resourceRegistry)
{
Expand All @@ -953,6 +954,21 @@ HdSt_PipelineDrawBatch::PrepareDraw(
_dispatchBuffer->CopyData(_drawCommandBuffer);
_drawCommandBufferDirty = false;
}

Hgi *hgi = resourceRegistry->GetHgi();
HgiCapabilities const *capabilities = hgi->GetCapabilities();

// For ICBs on Apple Silicon, we do not support rendering to non-MSAA
// surfaces, such as OIT as Volumetrics. Disable in these cases.
bool const drawICB =
capabilities->IsSet(HgiDeviceCapabilitiesBitsIndirectCommandBuffers) &&
gfxCmds && // Prevent the imageShaderRenderPass from using ICBs
renderPassState->GetMultiSampleEnabled();

_indirectCommands.reset();
if (drawICB) {
_PrepareIndirectCommandBuffer(renderPassState, resourceRegistry);
}

if (_useGpuCulling) {
// Ignore passed in gfxCmds for now since GPU frustum culling
Expand Down Expand Up @@ -1235,56 +1251,69 @@ HdSt_PipelineDrawBatch::ExecuteDraw(
if (!TF_VERIFY(_dispatchBuffer)) return;

if (_HasNothingToDraw()) return;

HgiCapabilities const *capabilities =
resourceRegistry->GetHgi()->GetCapabilities();

// Drawing can be either direct or indirect. For either case,
// the drawing batch and drawing program are prepared to resolve
// drawing coordinate state indirectly, i.e. from buffer data.
bool const drawIndirect =
capabilities->IsSet(HgiDeviceCapabilitiesBitsMultiDrawIndirect);
_DrawingProgram & program = _GetDrawingProgram(renderPassState,
resourceRegistry);
if (!TF_VERIFY(program.IsValid())) return;

_BindingState state(
_drawItemInstances.front()->GetDrawItem(),
_dispatchBuffer,
program.GetBinder(),
program.GetGLSLProgram(),
program.GetComposedShaders(),
program.GetGeometricShader());

Hgi * hgi = resourceRegistry->GetHgi();

HgiGraphicsPipelineSharedPtr pso =
_GetDrawPipeline(
renderPassState,
resourceRegistry,
state);

HgiGraphicsPipelineHandle psoHandle = *pso.get();
gfxCmds->BindPipeline(psoHandle);

HgiResourceBindingsDesc bindingsDesc;
state.GetBindingsForDrawing(&bindingsDesc);
Hgi *hgi = resourceRegistry->GetHgi();
HgiCapabilities const *capabilities = hgi->GetCapabilities();

HgiResourceBindingsHandle resourceBindings =
hgi->CreateResourceBindings(bindingsDesc);
gfxCmds->BindResources(resourceBindings);
//
// If an indirect command buffer was created in the Prepare phase then
// execute it here. Otherwise render with the normal graphicsCmd path.
//
if (_indirectCommands) {
HgiIndirectCommandEncoder *encoder = hgi->GetIndirectCommandEncoder();
encoder->ExecuteDraw(gfxCmds, _indirectCommands.get());

HgiVertexBufferBindingVector bindings;
_GetVertexBufferBindingsForDrawing(&bindings, state);
gfxCmds->BindVertexBuffers(bindings);

if (drawIndirect) {
_ExecuteDrawIndirect(gfxCmds, state.indexBar);
} else {
_ExecuteDrawImmediate(gfxCmds, state.indexBar);
hgi->DestroyResourceBindings(&(_indirectCommands->resourceBindings));
_indirectCommands.reset();
}
else {
_DrawingProgram & program = _GetDrawingProgram(renderPassState,
resourceRegistry);
if (!TF_VERIFY(program.IsValid())) return;

_BindingState state(
_drawItemInstances.front()->GetDrawItem(),
_dispatchBuffer,
program.GetBinder(),
program.GetGLSLProgram(),
program.GetComposedShaders(),
program.GetGeometricShader());

HgiGraphicsPipelineSharedPtr pso =
_GetDrawPipeline(
renderPassState,
resourceRegistry,
state);

HgiGraphicsPipelineHandle psoHandle = *pso.get();
gfxCmds->BindPipeline(psoHandle);

HgiResourceBindingsDesc bindingsDesc;
state.GetBindingsForDrawing(&bindingsDesc);

HgiResourceBindingsHandle resourceBindings =
hgi->CreateResourceBindings(bindingsDesc);
gfxCmds->BindResources(resourceBindings);

HgiVertexBufferBindingVector bindings;
_GetVertexBufferBindingsForDrawing(&bindings, state);
gfxCmds->BindVertexBuffers(bindings);

// Drawing can be either direct or indirect. For either case,
// the drawing batch and drawing program are prepared to resolve
// drawing coordinate state indirectly, i.e. from buffer data.
bool const drawIndirect =
capabilities->IsSet(HgiDeviceCapabilitiesBitsMultiDrawIndirect);

if (drawIndirect) {
_ExecuteDrawIndirect(gfxCmds, state.indexBar);
} else {
_ExecuteDrawImmediate(gfxCmds, state.indexBar);
}

hgi->DestroyResourceBindings(&resourceBindings);
hgi->DestroyResourceBindings(&resourceBindings);
}

HD_PERF_COUNTER_INCR(HdPerfTokens->drawCalls);
HD_PERF_COUNTER_ADD(HdTokens->itemsDrawn, _numVisibleItems);
Expand Down Expand Up @@ -1430,6 +1459,75 @@ _GetCullPipeline(
return pipelineInstance.GetValue();
}

void
HdSt_PipelineDrawBatch::_PrepareIndirectCommandBuffer(
HdStRenderPassStateSharedPtr const & renderPassState,
HdStResourceRegistrySharedPtr const & resourceRegistry)
{
Hgi *hgi = resourceRegistry->GetHgi();
_DrawingProgram & program = _GetDrawingProgram(renderPassState,
resourceRegistry);
if (!TF_VERIFY(program.IsValid())) return;

_BindingState state(
_drawItemInstances.front()->GetDrawItem(),
_dispatchBuffer,
program.GetBinder(),
program.GetGLSLProgram(),
program.GetComposedShaders(),
program.GetGeometricShader());

HgiGraphicsPipelineSharedPtr pso =
_GetDrawPipeline(
renderPassState,
resourceRegistry,
state);

HgiGraphicsPipelineHandle psoHandle = *pso.get();

HgiResourceBindingsDesc bindingsDesc;
state.GetBindingsForDrawing(&bindingsDesc);

HgiResourceBindingsHandle resourceBindings =
hgi->CreateResourceBindings(bindingsDesc);

HgiVertexBufferBindingVector vertexBindings;
_GetVertexBufferBindingsForDrawing(&vertexBindings, state);

HdStBufferResourceSharedPtr paramBuffer = _dispatchBuffer->
GetBufferArrayRange()->GetResource(HdTokens->drawDispatch);

HgiIndirectCommandEncoder *encoder = hgi->GetIndirectCommandEncoder();
HgiComputeCmds *computeCmds = resourceRegistry->GetGlobalComputeCmds();

if (!_useDrawIndexed) {
_indirectCommands = encoder->EncodeDraw(
computeCmds,
psoHandle,
resourceBindings,
vertexBindings,
paramBuffer->GetHandle(),
paramBuffer->GetOffset(),
_dispatchBuffer->GetCount(),
paramBuffer->GetStride());
} else {
HdStBufferResourceSharedPtr indexBuffer =
state.indexBar->GetResource(HdTokens->indices);

_indirectCommands = encoder->EncodeDrawIndexed(
computeCmds,
psoHandle,
resourceBindings,
vertexBindings,
indexBuffer->GetHandle(),
paramBuffer->GetHandle(),
paramBuffer->GetOffset(),
_dispatchBuffer->GetCount(),
paramBuffer->GetStride(),
_patchBaseVertexByteOffset);
}
}

void
HdSt_PipelineDrawBatch::_ExecuteFrustumCull(
bool const updateBufferData,
Expand Down
7 changes: 7 additions & 0 deletions pxr/imaging/hdSt/pipelineDrawBatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
PXR_NAMESPACE_OPEN_SCOPE

class HgiCapabilities;
struct HgiIndirectCommands;
using HdBindingRequestVector = std::vector<HdBindingRequest>;

/// \class HdSt_PipelineDrawBatch
Expand Down Expand Up @@ -128,6 +129,10 @@ class HdSt_PipelineDrawBatch : public HdSt_DrawBatch
HdStResourceRegistrySharedPtr const & resourceRegistry);

void _CompileBatch(HdStResourceRegistrySharedPtr const & resourceRegistry);

void _PrepareIndirectCommandBuffer(
HdStRenderPassStateSharedPtr const & renderPassState,
HdStResourceRegistrySharedPtr const & resourceRegistry);

bool _HasNothingToDraw() const;

Expand Down Expand Up @@ -179,6 +184,8 @@ class HdSt_PipelineDrawBatch : public HdSt_DrawBatch
size_t _instanceCountOffset;
size_t _cullInstanceCountOffset;
size_t _patchBaseVertexByteOffset;

std::unique_ptr<struct HgiIndirectCommands> _indirectCommands;
};


Expand Down
1 change: 1 addition & 0 deletions pxr/imaging/hgi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pxr_library(hgi
graphicsCmdsDesc
graphicsPipeline
hgi
indirectCommandEncoder
resourceBindings
sampler
shaderFunction
Expand Down
4 changes: 4 additions & 0 deletions pxr/imaging/hgi/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ using HgiBits = uint32_t;
/// <li>HgiDeviceCapabilitiesBitsBasePrimitiveOffset:
/// The device requires workaround for base primitive offset</li>
/// </ul>
/// <li>HgiDeviceCapabilitiesBitsIndirectCommandBuffers:
/// Indirect command buffers are supported</li>
/// </ul>
///
enum HgiDeviceCapabilitiesBits : HgiBits
{
Expand All @@ -91,6 +94,7 @@ enum HgiDeviceCapabilitiesBits : HgiBits
HgiDeviceCapabilitiesBitsCustomDepthRange = 1 << 13,
HgiDeviceCapabilitiesBitsMetalTessellation = 1 << 14,
HgiDeviceCapabilitiesBitsBasePrimitiveOffset = 1 << 15,
HgiDeviceCapabilitiesBitsIndirectCommandBuffers = 1 << 16,
};

using HgiDeviceCapabilities = HgiBits;
Expand Down
1 change: 1 addition & 0 deletions pxr/imaging/hgi/graphicsCmds.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ class HgiGraphicsCmds : public HgiCmds
HGI_API
virtual void MemoryBarrier(HgiMemoryBarrier barrier) = 0;


protected:
HGI_API
HgiGraphicsCmds();
Expand Down
4 changes: 4 additions & 0 deletions pxr/imaging/hgi/hgi.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
PXR_NAMESPACE_OPEN_SCOPE

class HgiCapabilities;
class HgiIndirectCommandEncoder;

using HgiUniquePtr = std::unique_ptr<class Hgi>;

Expand Down Expand Up @@ -295,6 +296,9 @@ class Hgi
HGI_API
virtual HgiCapabilities const* GetCapabilities() const = 0;

HGI_API
virtual HgiIndirectCommandEncoder* GetIndirectCommandEncoder() = 0;

/// Optionally called by client app at the start of a new rendering frame.
/// We can't rely on StartFrame for anything important, because it is up to
/// the external client to (optionally) call this and they may never do.
Expand Down
31 changes: 31 additions & 0 deletions pxr/imaging/hgi/indirectCommandEncoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// Copyright 2022 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//

#include "pxr/imaging/hgi/indirectCommandEncoder.h"

PXR_NAMESPACE_OPEN_SCOPE

HgiIndirectCommandEncoder::~HgiIndirectCommandEncoder() = default;

PXR_NAMESPACE_CLOSE_SCOPE
Loading