mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-02 11:25:02 +00:00
This patch fixes a bug in the computation of temporary buffer size in the implementation of the workaround for the T2T copy issue on Intel GPUs. With this tests all the T2T operation tests in WebGPU CTS will be able to pass on Intel Gen9 and Gen9.5 GPUs. BUG=chromium:1161355 TEST=dawn_end2end_tests Change-Id: Ic4fb0c0056cf7b01720e47cc91c37a9f293cd194 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/44840 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
1577 lines
78 KiB
C++
1577 lines
78 KiB
C++
// Copyright 2017 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "dawn_native/d3d12/CommandBufferD3D12.h"
|
|
|
|
#include "dawn_native/BindGroupTracker.h"
|
|
#include "dawn_native/CommandValidation.h"
|
|
#include "dawn_native/RenderBundle.h"
|
|
#include "dawn_native/d3d12/BindGroupD3D12.h"
|
|
#include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
|
|
#include "dawn_native/d3d12/ComputePipelineD3D12.h"
|
|
#include "dawn_native/d3d12/DeviceD3D12.h"
|
|
#include "dawn_native/d3d12/PipelineLayoutD3D12.h"
|
|
#include "dawn_native/d3d12/PlatformFunctions.h"
|
|
#include "dawn_native/d3d12/QuerySetD3D12.h"
|
|
#include "dawn_native/d3d12/RenderPassBuilderD3D12.h"
|
|
#include "dawn_native/d3d12/RenderPipelineD3D12.h"
|
|
#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
|
|
#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h"
|
|
#include "dawn_native/d3d12/UtilsD3D12.h"
|
|
|
|
namespace dawn_native { namespace d3d12 {
|
|
|
|
namespace {
|
|
|
|
DXGI_FORMAT DXGIIndexFormat(wgpu::IndexFormat format) {
|
|
switch (format) {
|
|
case wgpu::IndexFormat::Undefined:
|
|
return DXGI_FORMAT_UNKNOWN;
|
|
case wgpu::IndexFormat::Uint16:
|
|
return DXGI_FORMAT_R16_UINT;
|
|
case wgpu::IndexFormat::Uint32:
|
|
return DXGI_FORMAT_R32_UINT;
|
|
}
|
|
}
|
|
|
|
D3D12_QUERY_TYPE D3D12QueryType(wgpu::QueryType type) {
|
|
switch (type) {
|
|
case wgpu::QueryType::Occlusion:
|
|
return D3D12_QUERY_TYPE_BINARY_OCCLUSION;
|
|
case wgpu::QueryType::PipelineStatistics:
|
|
return D3D12_QUERY_TYPE_PIPELINE_STATISTICS;
|
|
case wgpu::QueryType::Timestamp:
|
|
return D3D12_QUERY_TYPE_TIMESTAMP;
|
|
}
|
|
}
|
|
|
|
bool CanUseCopyResource(const TextureCopy& src,
|
|
const TextureCopy& dst,
|
|
const Extent3D& copySize) {
|
|
// Checked by validation
|
|
ASSERT(src.texture->GetSampleCount() == dst.texture->GetSampleCount());
|
|
ASSERT(src.texture->GetFormat().format == dst.texture->GetFormat().format);
|
|
ASSERT(src.aspect == dst.aspect);
|
|
|
|
const Extent3D& srcSize = src.texture->GetSize();
|
|
const Extent3D& dstSize = dst.texture->GetSize();
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-copyresource
|
|
// In order to use D3D12's copy resource, the textures must be the same dimensions, and
|
|
// the copy must be of the entire resource.
|
|
// TODO(dawn:129): Support 1D textures.
|
|
return src.aspect == src.texture->GetFormat().aspects &&
|
|
src.texture->GetDimension() == dst.texture->GetDimension() && //
|
|
dst.texture->GetNumMipLevels() == 1 && //
|
|
src.texture->GetNumMipLevels() == 1 && // A copy command is of a single mip, so
|
|
// if a resource has more than one, we
|
|
// definitely cannot use CopyResource.
|
|
copySize.width == dstSize.width && //
|
|
copySize.width == srcSize.width && //
|
|
copySize.height == dstSize.height && //
|
|
copySize.height == srcSize.height && //
|
|
copySize.depth == dstSize.depth && //
|
|
copySize.depth == srcSize.depth;
|
|
}
|
|
|
|
void RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList,
|
|
const Texture2DCopySplit& baseCopySplit,
|
|
Buffer* buffer,
|
|
uint64_t baseOffset,
|
|
uint64_t bufferBytesPerRow,
|
|
Texture* texture,
|
|
uint32_t textureMiplevel,
|
|
uint32_t textureSlice,
|
|
Aspect aspect) {
|
|
const D3D12_TEXTURE_COPY_LOCATION textureLocation =
|
|
ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice,
|
|
aspect);
|
|
|
|
const uint64_t offset = baseCopySplit.offset + baseOffset;
|
|
|
|
for (uint32_t i = 0; i < baseCopySplit.count; ++i) {
|
|
const Texture2DCopySplit::CopyInfo& info = baseCopySplit.copies[i];
|
|
|
|
// TODO(jiawei.shao@intel.com): pre-compute bufferLocation and sourceRegion as
|
|
// members in Texture2DCopySplit::CopyInfo.
|
|
const D3D12_TEXTURE_COPY_LOCATION bufferLocation =
|
|
ComputeBufferLocationForCopyTextureRegion(texture, buffer->GetD3D12Resource(),
|
|
info.bufferSize, offset,
|
|
bufferBytesPerRow, aspect);
|
|
const D3D12_BOX sourceRegion =
|
|
ComputeD3D12BoxFromOffsetAndSize(info.textureOffset, info.copySize);
|
|
|
|
commandList->CopyTextureRegion(&bufferLocation, info.bufferOffset.x,
|
|
info.bufferOffset.y, info.bufferOffset.z,
|
|
&textureLocation, &sourceRegion);
|
|
}
|
|
}
|
|
|
|
void CopyTextureToBufferWithCopySplit(ID3D12GraphicsCommandList* commandList,
|
|
const TextureCopy& textureCopy,
|
|
const BufferCopy& bufferCopy,
|
|
Texture* texture,
|
|
Buffer* buffer,
|
|
const Extent3D& copySize) {
|
|
const TexelBlockInfo& blockInfo =
|
|
texture->GetFormat().GetAspectInfo(textureCopy.aspect).block;
|
|
|
|
// See comments around ComputeTextureCopySplits() for more details.
|
|
const TextureCopySplits copySplits =
|
|
ComputeTextureCopySplits(textureCopy.origin, copySize, blockInfo, bufferCopy.offset,
|
|
bufferCopy.bytesPerRow, bufferCopy.rowsPerImage);
|
|
|
|
const uint64_t bytesPerSlice = bufferCopy.bytesPerRow * bufferCopy.rowsPerImage;
|
|
|
|
// copySplits.copies2D[1] is always calculated for the second copy slice with
|
|
// extra "bytesPerSlice" copy offset compared with the first copy slice. So
|
|
// here we use an array bufferOffsetsForNextSlice to record the extra offsets
|
|
// for each copy slice: bufferOffsetsForNextSlice[0] is the extra offset for
|
|
// the next copy slice that uses copySplits.copies2D[0], and
|
|
// bufferOffsetsForNextSlice[1] is the extra offset for the next copy slice
|
|
// that uses copySplits.copies2D[1].
|
|
std::array<uint64_t, TextureCopySplits::kMaxTextureCopySplits>
|
|
bufferOffsetsForNextSlice = {{0u, 0u}};
|
|
for (uint32_t copySlice = 0; copySlice < copySize.depth; ++copySlice) {
|
|
const uint32_t splitIndex = copySlice % copySplits.copies2D.size();
|
|
|
|
const Texture2DCopySplit& copySplitPerLayerBase = copySplits.copies2D[splitIndex];
|
|
const uint64_t bufferOffsetForNextSlice = bufferOffsetsForNextSlice[splitIndex];
|
|
const uint32_t copyTextureLayer = copySlice + textureCopy.origin.z;
|
|
|
|
RecordCopyTextureToBufferFromTextureCopySplit(
|
|
commandList, copySplitPerLayerBase, buffer, bufferOffsetForNextSlice,
|
|
bufferCopy.bytesPerRow, texture, textureCopy.mipLevel, copyTextureLayer,
|
|
textureCopy.aspect);
|
|
|
|
bufferOffsetsForNextSlice[splitIndex] += bytesPerSlice * copySplits.copies2D.size();
|
|
}
|
|
}
|
|
|
|
void RecordWriteTimestampCmd(ID3D12GraphicsCommandList* commandList,
|
|
WriteTimestampCmd* cmd) {
|
|
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
|
ASSERT(D3D12QueryType(querySet->GetQueryType()) == D3D12_QUERY_TYPE_TIMESTAMP);
|
|
commandList->EndQuery(querySet->GetQueryHeap(), D3D12_QUERY_TYPE_TIMESTAMP,
|
|
cmd->queryIndex);
|
|
}
|
|
|
|
void RecordFirstIndexOffset(ID3D12GraphicsCommandList* commandList,
|
|
RenderPipeline* pipeline,
|
|
uint32_t firstVertex,
|
|
uint32_t firstInstance) {
|
|
const FirstOffsetInfo& firstOffsetInfo = pipeline->GetFirstOffsetInfo();
|
|
if (!firstOffsetInfo.usesVertexIndex && !firstOffsetInfo.usesInstanceIndex) {
|
|
return;
|
|
}
|
|
std::array<uint32_t, 2> offsets{};
|
|
uint32_t count = 0;
|
|
if (firstOffsetInfo.usesVertexIndex) {
|
|
offsets[firstOffsetInfo.vertexIndexOffset / sizeof(uint32_t)] = firstVertex;
|
|
++count;
|
|
}
|
|
if (firstOffsetInfo.usesInstanceIndex) {
|
|
offsets[firstOffsetInfo.instanceIndexOffset / sizeof(uint32_t)] = firstInstance;
|
|
++count;
|
|
}
|
|
PipelineLayout* layout = ToBackend(pipeline->GetLayout());
|
|
commandList->SetGraphicsRoot32BitConstants(layout->GetFirstIndexOffsetParameterIndex(),
|
|
count, offsets.data(), 0);
|
|
}
|
|
|
|
bool ShouldCopyUsingTemporaryBuffer(DeviceBase* device,
|
|
const TextureCopy& srcCopy,
|
|
const TextureCopy& dstCopy) {
|
|
// Currently we only need the workaround for an Intel D3D12 driver issue.
|
|
if (device->IsToggleEnabled(
|
|
Toggle::
|
|
UseTempBufferInSmallFormatTextureToTextureCopyFromGreaterToLessMipLevel)) {
|
|
bool copyToLesserLevel = srcCopy.mipLevel > dstCopy.mipLevel;
|
|
ASSERT(srcCopy.texture->GetFormat().format == dstCopy.texture->GetFormat().format);
|
|
|
|
// GetAspectInfo(aspect) requires HasOneBit(aspect) == true, plus the texel block
|
|
// sizes of depth stencil formats are always no less than 4 bytes.
|
|
bool isSmallColorFormat =
|
|
HasOneBit(srcCopy.aspect) &&
|
|
srcCopy.texture->GetFormat().GetAspectInfo(srcCopy.aspect).block.byteSize < 4u;
|
|
if (copyToLesserLevel && isSmallColorFormat) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void RecordCopyTextureWithTemporaryBuffer(CommandRecordingContext* recordingContext,
|
|
const TextureCopy& srcCopy,
|
|
const TextureCopy& dstCopy,
|
|
const Extent3D& copySize) {
|
|
ASSERT(srcCopy.texture->GetFormat().format == dstCopy.texture->GetFormat().format);
|
|
ASSERT(srcCopy.aspect == dstCopy.aspect);
|
|
dawn_native::Format format = srcCopy.texture->GetFormat();
|
|
const TexelBlockInfo& blockInfo = format.GetAspectInfo(srcCopy.aspect).block;
|
|
ASSERT(copySize.width % blockInfo.width == 0);
|
|
uint32_t widthInBlocks = copySize.width / blockInfo.width;
|
|
ASSERT(copySize.height % blockInfo.height == 0);
|
|
uint32_t heightInBlocks = copySize.height / blockInfo.height;
|
|
|
|
// Create tempBuffer
|
|
uint32_t bytesPerRow =
|
|
Align(blockInfo.byteSize * widthInBlocks, kTextureBytesPerRowAlignment);
|
|
uint32_t rowsPerImage = heightInBlocks;
|
|
|
|
// The size of temporary buffer isn't needed to be a multiple of 4 because we don't
|
|
// need to set mappedAtCreation to be true.
|
|
auto tempBufferSize =
|
|
ComputeRequiredBytesInCopy(blockInfo, copySize, bytesPerRow, rowsPerImage);
|
|
|
|
BufferDescriptor tempBufferDescriptor;
|
|
tempBufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
tempBufferDescriptor.size = tempBufferSize.AcquireSuccess();
|
|
Device* device = ToBackend(srcCopy.texture->GetDevice());
|
|
Ref<Buffer> tempBuffer =
|
|
AcquireRef(ToBackend(device->CreateBuffer(&tempBufferDescriptor)));
|
|
|
|
// Copy from source texture into tempBuffer
|
|
Texture* srcTexture = ToBackend(srcCopy.texture).Get();
|
|
tempBuffer->TrackUsageAndTransitionNow(recordingContext, wgpu::BufferUsage::CopyDst);
|
|
BufferCopy bufferCopy;
|
|
bufferCopy.buffer = tempBuffer;
|
|
bufferCopy.offset = 0;
|
|
bufferCopy.bytesPerRow = bytesPerRow;
|
|
bufferCopy.rowsPerImage = rowsPerImage;
|
|
CopyTextureToBufferWithCopySplit(recordingContext->GetCommandList(), srcCopy,
|
|
bufferCopy, srcTexture, tempBuffer.Get(), copySize);
|
|
|
|
// Copy from tempBuffer into destination texture
|
|
tempBuffer->TrackUsageAndTransitionNow(recordingContext, wgpu::BufferUsage::CopySrc);
|
|
Texture* dstTexture = ToBackend(dstCopy.texture).Get();
|
|
CopyBufferToTextureWithCopySplit(recordingContext, dstCopy,
|
|
tempBuffer->GetD3D12Resource(), 0, bytesPerRow,
|
|
rowsPerImage, copySize, dstTexture, dstCopy.aspect);
|
|
|
|
// Save tempBuffer into recordingContext
|
|
recordingContext->AddToTempBuffers(std::move(tempBuffer));
|
|
}
|
|
} // anonymous namespace
|
|
|
|
class BindGroupStateTracker : public BindGroupTrackerBase<false, uint64_t> {
|
|
using Base = BindGroupTrackerBase;
|
|
|
|
public:
|
|
BindGroupStateTracker(Device* device)
|
|
: BindGroupTrackerBase(),
|
|
mDevice(device),
|
|
mViewAllocator(device->GetViewShaderVisibleDescriptorAllocator()),
|
|
mSamplerAllocator(device->GetSamplerShaderVisibleDescriptorAllocator()) {
|
|
}
|
|
|
|
void SetInComputePass(bool inCompute_) {
|
|
mInCompute = inCompute_;
|
|
}
|
|
|
|
void OnSetPipeline(PipelineBase* pipeline) {
|
|
// Invalidate the root sampler tables previously set in the root signature.
|
|
// This is because changing the pipeline layout also changes the root signature.
|
|
const PipelineLayout* pipelineLayout = ToBackend(pipeline->GetLayout());
|
|
if (mLastAppliedPipelineLayout != pipelineLayout) {
|
|
mBoundRootSamplerTables = {};
|
|
}
|
|
|
|
Base::OnSetPipeline(pipeline);
|
|
}
|
|
|
|
MaybeError Apply(CommandRecordingContext* commandContext) {
|
|
// Bindgroups are allocated in shader-visible descriptor heaps which are managed by a
|
|
// ringbuffer. There can be a single shader-visible descriptor heap of each type bound
|
|
// at any given time. This means that when we switch heaps, all other currently bound
|
|
// bindgroups must be re-populated. Bindgroups can fail allocation gracefully which is
|
|
// the signal to change the bounded heaps.
|
|
// Re-populating all bindgroups after the last one fails causes duplicated allocations
|
|
// to occur on overflow.
|
|
// TODO(bryan.bernhart@intel.com): Consider further optimization.
|
|
bool didCreateBindGroupViews = true;
|
|
bool didCreateBindGroupSamplers = true;
|
|
for (BindGroupIndex index : IterateBitSet(mDirtyBindGroups)) {
|
|
BindGroup* group = ToBackend(mBindGroups[index]);
|
|
didCreateBindGroupViews = group->PopulateViews(mViewAllocator);
|
|
didCreateBindGroupSamplers = group->PopulateSamplers(mDevice, mSamplerAllocator);
|
|
if (!didCreateBindGroupViews && !didCreateBindGroupSamplers) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
|
|
|
if (!didCreateBindGroupViews || !didCreateBindGroupSamplers) {
|
|
if (!didCreateBindGroupViews) {
|
|
DAWN_TRY(mViewAllocator->AllocateAndSwitchShaderVisibleHeap());
|
|
}
|
|
|
|
if (!didCreateBindGroupSamplers) {
|
|
DAWN_TRY(mSamplerAllocator->AllocateAndSwitchShaderVisibleHeap());
|
|
}
|
|
|
|
mDirtyBindGroupsObjectChangedOrIsDynamic |= mBindGroupLayoutsMask;
|
|
mDirtyBindGroups |= mBindGroupLayoutsMask;
|
|
|
|
// Must be called before applying the bindgroups.
|
|
SetID3D12DescriptorHeaps(commandList);
|
|
|
|
for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) {
|
|
BindGroup* group = ToBackend(mBindGroups[index]);
|
|
didCreateBindGroupViews = group->PopulateViews(mViewAllocator);
|
|
didCreateBindGroupSamplers =
|
|
group->PopulateSamplers(mDevice, mSamplerAllocator);
|
|
ASSERT(didCreateBindGroupViews);
|
|
ASSERT(didCreateBindGroupSamplers);
|
|
}
|
|
}
|
|
|
|
for (BindGroupIndex index : IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) {
|
|
BindGroup* group = ToBackend(mBindGroups[index]);
|
|
ApplyBindGroup(commandList, ToBackend(mPipelineLayout), index, group,
|
|
mDynamicOffsetCounts[index], mDynamicOffsets[index].data());
|
|
}
|
|
|
|
if (mInCompute) {
|
|
std::vector<D3D12_RESOURCE_BARRIER> barriers;
|
|
for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) {
|
|
BindGroupLayoutBase* layout = mBindGroups[index]->GetLayout();
|
|
for (BindingIndex binding{0}; binding < layout->GetBindingCount(); ++binding) {
|
|
const BindingInfo& bindingInfo = layout->GetBindingInfo(binding);
|
|
switch (bindingInfo.bindingType) {
|
|
case BindingInfoType::Buffer: {
|
|
D3D12_RESOURCE_BARRIER barrier;
|
|
wgpu::BufferUsage usage;
|
|
switch (bindingInfo.buffer.type) {
|
|
case wgpu::BufferBindingType::Uniform:
|
|
usage = wgpu::BufferUsage::Uniform;
|
|
break;
|
|
case wgpu::BufferBindingType::Storage:
|
|
usage = wgpu::BufferUsage::Storage;
|
|
break;
|
|
case wgpu::BufferBindingType::ReadOnlyStorage:
|
|
usage = kReadOnlyStorageBuffer;
|
|
break;
|
|
case wgpu::BufferBindingType::Undefined:
|
|
UNREACHABLE();
|
|
}
|
|
if (ToBackend(mBindGroups[index]
|
|
->GetBindingAsBufferBinding(binding)
|
|
.buffer)
|
|
->TrackUsageAndGetResourceBarrier(commandContext, &barrier,
|
|
usage)) {
|
|
barriers.push_back(barrier);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BindingInfoType::StorageTexture: {
|
|
TextureViewBase* view =
|
|
mBindGroups[index]->GetBindingAsTextureView(binding);
|
|
wgpu::TextureUsage usage;
|
|
switch (bindingInfo.storageTexture.access) {
|
|
case wgpu::StorageTextureAccess::ReadOnly:
|
|
usage = kReadOnlyStorageTexture;
|
|
break;
|
|
case wgpu::StorageTextureAccess::WriteOnly:
|
|
usage = wgpu::TextureUsage::Storage;
|
|
break;
|
|
case wgpu::StorageTextureAccess::Undefined:
|
|
UNREACHABLE();
|
|
}
|
|
ToBackend(view->GetTexture())
|
|
->TransitionUsageAndGetResourceBarrier(
|
|
commandContext, &barriers, usage,
|
|
view->GetSubresourceRange());
|
|
break;
|
|
}
|
|
|
|
case BindingInfoType::Texture: {
|
|
TextureViewBase* view =
|
|
mBindGroups[index]->GetBindingAsTextureView(binding);
|
|
ToBackend(view->GetTexture())
|
|
->TransitionUsageAndGetResourceBarrier(
|
|
commandContext, &barriers, wgpu::TextureUsage::Sampled,
|
|
view->GetSubresourceRange());
|
|
break;
|
|
}
|
|
|
|
case BindingInfoType::Sampler:
|
|
// Don't require barriers.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!barriers.empty()) {
|
|
commandList->ResourceBarrier(barriers.size(), barriers.data());
|
|
}
|
|
}
|
|
DidApply();
|
|
|
|
return {};
|
|
}
|
|
|
|
void SetID3D12DescriptorHeaps(ID3D12GraphicsCommandList* commandList) {
|
|
ASSERT(commandList != nullptr);
|
|
std::array<ID3D12DescriptorHeap*, 2> descriptorHeaps = {
|
|
mViewAllocator->GetShaderVisibleHeap(), mSamplerAllocator->GetShaderVisibleHeap()};
|
|
ASSERT(descriptorHeaps[0] != nullptr);
|
|
ASSERT(descriptorHeaps[1] != nullptr);
|
|
commandList->SetDescriptorHeaps(descriptorHeaps.size(), descriptorHeaps.data());
|
|
}
|
|
|
|
private:
|
|
void ApplyBindGroup(ID3D12GraphicsCommandList* commandList,
|
|
const PipelineLayout* pipelineLayout,
|
|
BindGroupIndex index,
|
|
BindGroup* group,
|
|
uint32_t dynamicOffsetCountIn,
|
|
const uint64_t* dynamicOffsetsIn) {
|
|
ityp::span<BindingIndex, const uint64_t> dynamicOffsets(
|
|
dynamicOffsetsIn, BindingIndex(dynamicOffsetCountIn));
|
|
ASSERT(dynamicOffsets.size() == group->GetLayout()->GetDynamicBufferCount());
|
|
|
|
// Usually, the application won't set the same offsets many times,
|
|
// so always try to apply dynamic offsets even if the offsets stay the same
|
|
if (dynamicOffsets.size() != BindingIndex(0)) {
|
|
// Update dynamic offsets.
|
|
// Dynamic buffer bindings are packed at the beginning of the layout.
|
|
for (BindingIndex bindingIndex{0}; bindingIndex < dynamicOffsets.size();
|
|
++bindingIndex) {
|
|
const BindingInfo& bindingInfo =
|
|
group->GetLayout()->GetBindingInfo(bindingIndex);
|
|
if (bindingInfo.visibility == wgpu::ShaderStage::None) {
|
|
// Skip dynamic buffers that are not visible. D3D12 does not have None
|
|
// visibility.
|
|
continue;
|
|
}
|
|
|
|
uint32_t parameterIndex =
|
|
pipelineLayout->GetDynamicRootParameterIndex(index, bindingIndex);
|
|
BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex);
|
|
|
|
// Calculate buffer locations that root descriptors links to. The location
|
|
// is (base buffer location + initial offset + dynamic offset)
|
|
uint64_t dynamicOffset = dynamicOffsets[bindingIndex];
|
|
uint64_t offset = binding.offset + dynamicOffset;
|
|
D3D12_GPU_VIRTUAL_ADDRESS bufferLocation =
|
|
ToBackend(binding.buffer)->GetVA() + offset;
|
|
|
|
ASSERT(bindingInfo.bindingType == BindingInfoType::Buffer);
|
|
switch (bindingInfo.buffer.type) {
|
|
case wgpu::BufferBindingType::Uniform:
|
|
if (mInCompute) {
|
|
commandList->SetComputeRootConstantBufferView(parameterIndex,
|
|
bufferLocation);
|
|
} else {
|
|
commandList->SetGraphicsRootConstantBufferView(parameterIndex,
|
|
bufferLocation);
|
|
}
|
|
break;
|
|
case wgpu::BufferBindingType::Storage:
|
|
if (mInCompute) {
|
|
commandList->SetComputeRootUnorderedAccessView(parameterIndex,
|
|
bufferLocation);
|
|
} else {
|
|
commandList->SetGraphicsRootUnorderedAccessView(parameterIndex,
|
|
bufferLocation);
|
|
}
|
|
break;
|
|
case wgpu::BufferBindingType::ReadOnlyStorage:
|
|
if (mInCompute) {
|
|
commandList->SetComputeRootShaderResourceView(parameterIndex,
|
|
bufferLocation);
|
|
} else {
|
|
commandList->SetGraphicsRootShaderResourceView(parameterIndex,
|
|
bufferLocation);
|
|
}
|
|
break;
|
|
case wgpu::BufferBindingType::Undefined:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
|
|
// It's not necessary to update descriptor tables if only the dynamic offset changed.
|
|
if (!mDirtyBindGroups[index]) {
|
|
return;
|
|
}
|
|
|
|
const uint32_t cbvUavSrvCount =
|
|
ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount();
|
|
const uint32_t samplerCount =
|
|
ToBackend(group->GetLayout())->GetSamplerDescriptorCount();
|
|
|
|
if (cbvUavSrvCount > 0) {
|
|
uint32_t parameterIndex = pipelineLayout->GetCbvUavSrvRootParameterIndex(index);
|
|
const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor = group->GetBaseViewDescriptor();
|
|
if (mInCompute) {
|
|
commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor);
|
|
} else {
|
|
commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor);
|
|
}
|
|
}
|
|
|
|
if (samplerCount > 0) {
|
|
uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index);
|
|
const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor =
|
|
group->GetBaseSamplerDescriptor();
|
|
// Check if the group requires its sampler table to be set in the pipeline.
|
|
// This because sampler heap allocations could be cached and use the same table.
|
|
if (mBoundRootSamplerTables[index].ptr != baseDescriptor.ptr) {
|
|
if (mInCompute) {
|
|
commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor);
|
|
} else {
|
|
commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor);
|
|
}
|
|
|
|
mBoundRootSamplerTables[index] = baseDescriptor;
|
|
}
|
|
}
|
|
}
|
|
|
|
Device* mDevice;
|
|
|
|
bool mInCompute = false;
|
|
|
|
ityp::array<BindGroupIndex, D3D12_GPU_DESCRIPTOR_HANDLE, kMaxBindGroups>
|
|
mBoundRootSamplerTables = {};
|
|
|
|
ShaderVisibleDescriptorAllocator* mViewAllocator;
|
|
ShaderVisibleDescriptorAllocator* mSamplerAllocator;
|
|
};
|
|
|
|
namespace {
|
|
class VertexBufferTracker {
|
|
public:
|
|
void OnSetVertexBuffer(VertexBufferSlot slot,
|
|
Buffer* buffer,
|
|
uint64_t offset,
|
|
uint64_t size) {
|
|
mStartSlot = std::min(mStartSlot, slot);
|
|
mEndSlot = std::max(mEndSlot, ityp::Add(slot, VertexBufferSlot(uint8_t(1))));
|
|
|
|
auto* d3d12BufferView = &mD3D12BufferViews[slot];
|
|
d3d12BufferView->BufferLocation = buffer->GetVA() + offset;
|
|
d3d12BufferView->SizeInBytes = size;
|
|
// The bufferView stride is set based on the vertex state before a draw.
|
|
}
|
|
|
|
void Apply(ID3D12GraphicsCommandList* commandList,
|
|
const RenderPipeline* renderPipeline) {
|
|
ASSERT(renderPipeline != nullptr);
|
|
|
|
VertexBufferSlot startSlot = mStartSlot;
|
|
VertexBufferSlot endSlot = mEndSlot;
|
|
|
|
// If the vertex state has changed, we need to update the StrideInBytes
|
|
// for the D3D12 buffer views. We also need to extend the dirty range to
|
|
// touch all these slots because the stride may have changed.
|
|
if (mLastAppliedRenderPipeline != renderPipeline) {
|
|
mLastAppliedRenderPipeline = renderPipeline;
|
|
|
|
for (VertexBufferSlot slot :
|
|
IterateBitSet(renderPipeline->GetVertexBufferSlotsUsed())) {
|
|
startSlot = std::min(startSlot, slot);
|
|
endSlot =
|
|
std::max(endSlot, ityp::Add(slot, VertexBufferSlot(uint8_t(1))));
|
|
mD3D12BufferViews[slot].StrideInBytes =
|
|
renderPipeline->GetVertexBuffer(slot).arrayStride;
|
|
}
|
|
}
|
|
|
|
if (endSlot <= startSlot) {
|
|
return;
|
|
}
|
|
|
|
// mD3D12BufferViews is kept up to date with the most recent data passed
|
|
// to SetVertexBuffer. This makes it correct to only track the start
|
|
// and end of the dirty range. When Apply is called,
|
|
// we will at worst set non-dirty vertex buffers in duplicate.
|
|
commandList->IASetVertexBuffers(static_cast<uint8_t>(startSlot),
|
|
static_cast<uint8_t>(ityp::Sub(endSlot, startSlot)),
|
|
&mD3D12BufferViews[startSlot]);
|
|
|
|
mStartSlot = VertexBufferSlot(kMaxVertexBuffers);
|
|
mEndSlot = VertexBufferSlot(uint8_t(0));
|
|
}
|
|
|
|
private:
|
|
// startSlot and endSlot indicate the range of dirty vertex buffers.
|
|
// If there are multiple calls to SetVertexBuffer, the start and end
|
|
// represent the union of the dirty ranges (the union may have non-dirty
|
|
// data in the middle of the range).
|
|
const RenderPipeline* mLastAppliedRenderPipeline = nullptr;
|
|
VertexBufferSlot mStartSlot{kMaxVertexBuffers};
|
|
VertexBufferSlot mEndSlot{uint8_t(0)};
|
|
ityp::array<VertexBufferSlot, D3D12_VERTEX_BUFFER_VIEW, kMaxVertexBuffers>
|
|
mD3D12BufferViews = {};
|
|
};
|
|
|
|
void ResolveMultisampledRenderPass(CommandRecordingContext* commandContext,
|
|
BeginRenderPassCmd* renderPass) {
|
|
ASSERT(renderPass != nullptr);
|
|
|
|
for (ColorAttachmentIndex i :
|
|
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
|
|
TextureViewBase* resolveTarget =
|
|
renderPass->colorAttachments[i].resolveTarget.Get();
|
|
if (resolveTarget == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
TextureViewBase* colorView = renderPass->colorAttachments[i].view.Get();
|
|
Texture* colorTexture = ToBackend(colorView->GetTexture());
|
|
Texture* resolveTexture = ToBackend(resolveTarget->GetTexture());
|
|
|
|
// Transition the usages of the color attachment and resolve target.
|
|
colorTexture->TrackUsageAndTransitionNow(commandContext,
|
|
D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
|
|
colorView->GetSubresourceRange());
|
|
resolveTexture->TrackUsageAndTransitionNow(commandContext,
|
|
D3D12_RESOURCE_STATE_RESOLVE_DEST,
|
|
resolveTarget->GetSubresourceRange());
|
|
|
|
// Do MSAA resolve with ResolveSubResource().
|
|
ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource();
|
|
ID3D12Resource* resolveTextureHandle = resolveTexture->GetD3D12Resource();
|
|
const uint32_t resolveTextureSubresourceIndex = resolveTexture->GetSubresourceIndex(
|
|
resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer(),
|
|
Aspect::Color);
|
|
constexpr uint32_t kColorTextureSubresourceIndex = 0;
|
|
commandContext->GetCommandList()->ResolveSubresource(
|
|
resolveTextureHandle, resolveTextureSubresourceIndex, colorTextureHandle,
|
|
kColorTextureSubresourceIndex, colorTexture->GetD3D12Format());
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
|
: CommandBufferBase(encoder, descriptor) {
|
|
}
|
|
|
|
MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext) {
|
|
Device* device = ToBackend(GetDevice());
|
|
BindGroupStateTracker bindingTracker(device);
|
|
|
|
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
|
|
|
// Make sure we use the correct descriptors for this command list. Could be done once per
|
|
// actual command list but here is ok because there should be few command buffers.
|
|
bindingTracker.SetID3D12DescriptorHeaps(commandList);
|
|
|
|
// Records the necessary barriers for the resource usage pre-computed by the frontend
|
|
auto PrepareResourcesForRenderPass = [](CommandRecordingContext* commandContext,
|
|
const PassResourceUsage& usages) -> bool {
|
|
std::vector<D3D12_RESOURCE_BARRIER> barriers;
|
|
|
|
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
|
|
|
wgpu::BufferUsage bufferUsages = wgpu::BufferUsage::None;
|
|
|
|
for (size_t i = 0; i < usages.buffers.size(); ++i) {
|
|
Buffer* buffer = ToBackend(usages.buffers[i]);
|
|
|
|
// TODO(jiawei.shao@intel.com): clear storage buffers with
|
|
// ClearUnorderedAccessView*().
|
|
buffer->GetDevice()->ConsumedError(buffer->EnsureDataInitialized(commandContext));
|
|
|
|
D3D12_RESOURCE_BARRIER barrier;
|
|
if (buffer->TrackUsageAndGetResourceBarrier(commandContext, &barrier,
|
|
usages.bufferUsages[i])) {
|
|
barriers.push_back(barrier);
|
|
}
|
|
bufferUsages |= usages.bufferUsages[i];
|
|
}
|
|
|
|
wgpu::TextureUsage textureUsages = wgpu::TextureUsage::None;
|
|
|
|
for (size_t i = 0; i < usages.textures.size(); ++i) {
|
|
Texture* texture = ToBackend(usages.textures[i]);
|
|
|
|
// Clear subresources that are not render attachments. Render attachments will be
|
|
// cleared in RecordBeginRenderPass by setting the loadop to clear when the texture
|
|
// subresource has not been initialized before the render pass.
|
|
usages.textureUsages[i].Iterate(
|
|
[&](const SubresourceRange& range, wgpu::TextureUsage usage) {
|
|
if (usage & ~wgpu::TextureUsage::RenderAttachment) {
|
|
texture->EnsureSubresourceContentInitialized(commandContext, range);
|
|
}
|
|
textureUsages |= usage;
|
|
});
|
|
|
|
ToBackend(usages.textures[i])
|
|
->TrackUsageAndGetResourceBarrierForPass(commandContext, &barriers,
|
|
usages.textureUsages[i]);
|
|
}
|
|
|
|
if (barriers.size()) {
|
|
commandList->ResourceBarrier(barriers.size(), barriers.data());
|
|
}
|
|
|
|
return (bufferUsages & wgpu::BufferUsage::Storage ||
|
|
textureUsages & wgpu::TextureUsage::Storage);
|
|
};
|
|
|
|
// TODO(jiawei.shao@intel.com): move the resource lazy clearing inside the barrier tracking
|
|
// for compute passes.
|
|
auto PrepareResourcesForComputePass = [](CommandRecordingContext* commandContext,
|
|
const PassResourceUsage& usages) -> void {
|
|
for (size_t i = 0; i < usages.buffers.size(); ++i) {
|
|
Buffer* buffer = ToBackend(usages.buffers[i]);
|
|
|
|
// TODO(jiawei.shao@intel.com): clear storage buffers with
|
|
// ClearUnorderedAccessView*().
|
|
buffer->GetDevice()->ConsumedError(buffer->EnsureDataInitialized(commandContext));
|
|
}
|
|
|
|
for (size_t i = 0; i < usages.textures.size(); ++i) {
|
|
Texture* texture = ToBackend(usages.textures[i]);
|
|
texture->EnsureSubresourceContentInitialized(commandContext,
|
|
texture->GetAllSubresources());
|
|
}
|
|
};
|
|
|
|
const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
|
|
uint32_t nextPassNumber = 0;
|
|
|
|
Command type;
|
|
while (mCommands.NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::BeginComputePass: {
|
|
mCommands.NextCommand<BeginComputePassCmd>();
|
|
|
|
PrepareResourcesForComputePass(commandContext,
|
|
passResourceUsages[nextPassNumber]);
|
|
bindingTracker.SetInComputePass(true);
|
|
DAWN_TRY(RecordComputePass(commandContext, &bindingTracker));
|
|
|
|
nextPassNumber++;
|
|
break;
|
|
}
|
|
|
|
case Command::BeginRenderPass: {
|
|
BeginRenderPassCmd* beginRenderPassCmd =
|
|
mCommands.NextCommand<BeginRenderPassCmd>();
|
|
|
|
const bool passHasUAV = PrepareResourcesForRenderPass(
|
|
commandContext, passResourceUsages[nextPassNumber]);
|
|
bindingTracker.SetInComputePass(false);
|
|
|
|
LazyClearRenderPassAttachments(beginRenderPassCmd);
|
|
DAWN_TRY(RecordRenderPass(commandContext, &bindingTracker, beginRenderPassCmd,
|
|
passHasUAV));
|
|
|
|
nextPassNumber++;
|
|
break;
|
|
}
|
|
|
|
case Command::CopyBufferToBuffer: {
|
|
CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
|
|
Buffer* srcBuffer = ToBackend(copy->source.Get());
|
|
Buffer* dstBuffer = ToBackend(copy->destination.Get());
|
|
|
|
DAWN_TRY(srcBuffer->EnsureDataInitialized(commandContext));
|
|
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(
|
|
commandContext, copy->destinationOffset, copy->size));
|
|
|
|
srcBuffer->TrackUsageAndTransitionNow(commandContext,
|
|
wgpu::BufferUsage::CopySrc);
|
|
dstBuffer->TrackUsageAndTransitionNow(commandContext,
|
|
wgpu::BufferUsage::CopyDst);
|
|
|
|
commandList->CopyBufferRegion(
|
|
dstBuffer->GetD3D12Resource(), copy->destinationOffset,
|
|
srcBuffer->GetD3D12Resource(), copy->sourceOffset, copy->size);
|
|
break;
|
|
}
|
|
|
|
case Command::CopyBufferToTexture: {
|
|
CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
|
|
Buffer* buffer = ToBackend(copy->source.buffer.Get());
|
|
Texture* texture = ToBackend(copy->destination.texture.Get());
|
|
|
|
DAWN_TRY(buffer->EnsureDataInitialized(commandContext));
|
|
|
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
|
SubresourceRange subresources =
|
|
GetSubresourcesAffectedByCopy(copy->destination, copy->copySize);
|
|
|
|
if (IsCompleteSubresourceCopiedTo(texture, copy->copySize,
|
|
copy->destination.mipLevel)) {
|
|
texture->SetIsSubresourceContentInitialized(true, subresources);
|
|
} else {
|
|
texture->EnsureSubresourceContentInitialized(commandContext, subresources);
|
|
}
|
|
|
|
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc);
|
|
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst,
|
|
subresources);
|
|
|
|
// compute the copySplits and record the CopyTextureRegion commands
|
|
CopyBufferToTextureWithCopySplit(
|
|
commandContext, copy->destination, buffer->GetD3D12Resource(),
|
|
copy->source.offset, copy->source.bytesPerRow, copy->source.rowsPerImage,
|
|
copy->copySize, texture, subresources.aspects);
|
|
|
|
break;
|
|
}
|
|
|
|
case Command::CopyTextureToBuffer: {
|
|
CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
|
|
Texture* texture = ToBackend(copy->source.texture.Get());
|
|
Buffer* buffer = ToBackend(copy->destination.buffer.Get());
|
|
|
|
DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy));
|
|
|
|
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
|
|
SubresourceRange subresources =
|
|
GetSubresourcesAffectedByCopy(copy->source, copy->copySize);
|
|
|
|
texture->EnsureSubresourceContentInitialized(commandContext, subresources);
|
|
|
|
texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc,
|
|
subresources);
|
|
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
|
|
|
|
CopyTextureToBufferWithCopySplit(commandList, copy->source, copy->destination,
|
|
texture, buffer, copy->copySize);
|
|
|
|
break;
|
|
}
|
|
|
|
case Command::CopyTextureToTexture: {
|
|
CopyTextureToTextureCmd* copy =
|
|
mCommands.NextCommand<CopyTextureToTextureCmd>();
|
|
|
|
Texture* source = ToBackend(copy->source.texture.Get());
|
|
Texture* destination = ToBackend(copy->destination.texture.Get());
|
|
|
|
SubresourceRange srcRange =
|
|
GetSubresourcesAffectedByCopy(copy->source, copy->copySize);
|
|
SubresourceRange dstRange =
|
|
GetSubresourcesAffectedByCopy(copy->destination, copy->copySize);
|
|
|
|
source->EnsureSubresourceContentInitialized(commandContext, srcRange);
|
|
if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
|
|
copy->destination.mipLevel)) {
|
|
destination->SetIsSubresourceContentInitialized(true, dstRange);
|
|
} else {
|
|
destination->EnsureSubresourceContentInitialized(commandContext, dstRange);
|
|
}
|
|
|
|
if (copy->source.texture.Get() == copy->destination.texture.Get() &&
|
|
copy->source.mipLevel == copy->destination.mipLevel) {
|
|
// When there are overlapped subresources, the layout of the overlapped
|
|
// subresources should all be COMMON instead of what we set now. Currently
|
|
// it is not allowed to copy with overlapped subresources, but we still
|
|
// add the ASSERT here as a reminder for this possible misuse.
|
|
ASSERT(!IsRangeOverlapped(copy->source.origin.z, copy->destination.origin.z,
|
|
copy->copySize.depth));
|
|
}
|
|
source->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc,
|
|
srcRange);
|
|
destination->TrackUsageAndTransitionNow(commandContext,
|
|
wgpu::TextureUsage::CopyDst, dstRange);
|
|
|
|
ASSERT(srcRange.aspects == dstRange.aspects);
|
|
if (ShouldCopyUsingTemporaryBuffer(GetDevice(), copy->source,
|
|
copy->destination)) {
|
|
RecordCopyTextureWithTemporaryBuffer(commandContext, copy->source,
|
|
copy->destination, copy->copySize);
|
|
break;
|
|
}
|
|
|
|
if (CanUseCopyResource(copy->source, copy->destination, copy->copySize)) {
|
|
commandList->CopyResource(destination->GetD3D12Resource(),
|
|
source->GetD3D12Resource());
|
|
} else {
|
|
// TODO(jiawei.shao@intel.com): support copying with 1D and 3D textures.
|
|
ASSERT(source->GetDimension() == wgpu::TextureDimension::e2D &&
|
|
destination->GetDimension() == wgpu::TextureDimension::e2D);
|
|
const dawn_native::Extent3D copyExtentOneSlice = {
|
|
copy->copySize.width, copy->copySize.height, 1u};
|
|
|
|
for (Aspect aspect : IterateEnumMask(srcRange.aspects)) {
|
|
for (uint32_t slice = 0; slice < copy->copySize.depth; ++slice) {
|
|
D3D12_TEXTURE_COPY_LOCATION srcLocation =
|
|
ComputeTextureCopyLocationForTexture(
|
|
source, copy->source.mipLevel,
|
|
copy->source.origin.z + slice, aspect);
|
|
|
|
D3D12_TEXTURE_COPY_LOCATION dstLocation =
|
|
ComputeTextureCopyLocationForTexture(
|
|
destination, copy->destination.mipLevel,
|
|
copy->destination.origin.z + slice, aspect);
|
|
|
|
Origin3D sourceOriginInSubresource = copy->source.origin;
|
|
sourceOriginInSubresource.z = 0;
|
|
D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize(
|
|
sourceOriginInSubresource, copyExtentOneSlice);
|
|
|
|
commandList->CopyTextureRegion(
|
|
&dstLocation, copy->destination.origin.x,
|
|
copy->destination.origin.y, 0, &srcLocation, &sourceRegion);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::ResolveQuerySet: {
|
|
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
|
|
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
|
Buffer* destination = ToBackend(cmd->destination.Get());
|
|
|
|
DAWN_TRY(destination->EnsureDataInitializedAsDestination(
|
|
commandContext, cmd->destinationOffset,
|
|
cmd->queryCount * sizeof(uint64_t)));
|
|
destination->TrackUsageAndTransitionNow(commandContext,
|
|
wgpu::BufferUsage::CopyDst);
|
|
|
|
commandList->ResolveQueryData(
|
|
querySet->GetQueryHeap(), D3D12QueryType(querySet->GetQueryType()),
|
|
cmd->firstQuery, cmd->queryCount, destination->GetD3D12Resource(),
|
|
cmd->destinationOffset);
|
|
|
|
// TODO(hao.x.li@intel.com): Add compute shader to convert the query result
|
|
// (ticks) to timestamp (ns)
|
|
|
|
break;
|
|
}
|
|
|
|
case Command::WriteTimestamp: {
|
|
WriteTimestampCmd* cmd = mCommands.NextCommand<WriteTimestampCmd>();
|
|
|
|
RecordWriteTimestampCmd(commandList, cmd);
|
|
break;
|
|
}
|
|
|
|
case Command::InsertDebugMarker: {
|
|
InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
|
|
const char* label = mCommands.NextData<char>(cmd->length + 1);
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
// PIX color is 1 byte per channel in ARGB format
|
|
constexpr uint64_t kPIXBlackColor = 0xff000000;
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::PopDebugGroup: {
|
|
mCommands.NextCommand<PopDebugGroupCmd>();
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixEndEventOnCommandList(commandList);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::PushDebugGroup: {
|
|
PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
|
|
const char* label = mCommands.NextData<char>(cmd->length + 1);
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
// PIX color is 1 byte per channel in ARGB format
|
|
constexpr uint64_t kPIXBlackColor = 0xff000000;
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError CommandBuffer::RecordComputePass(CommandRecordingContext* commandContext,
|
|
BindGroupStateTracker* bindingTracker) {
|
|
PipelineLayout* lastLayout = nullptr;
|
|
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
|
|
|
Command type;
|
|
while (mCommands.NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::Dispatch: {
|
|
DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>();
|
|
|
|
DAWN_TRY(bindingTracker->Apply(commandContext));
|
|
commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z);
|
|
break;
|
|
}
|
|
|
|
case Command::DispatchIndirect: {
|
|
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
|
|
|
|
DAWN_TRY(bindingTracker->Apply(commandContext));
|
|
Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get());
|
|
buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::Indirect);
|
|
ComPtr<ID3D12CommandSignature> signature =
|
|
ToBackend(GetDevice())->GetDispatchIndirectSignature();
|
|
commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource(),
|
|
dispatch->indirectOffset, nullptr, 0);
|
|
break;
|
|
}
|
|
|
|
case Command::EndComputePass: {
|
|
mCommands.NextCommand<EndComputePassCmd>();
|
|
return {};
|
|
}
|
|
|
|
case Command::SetComputePipeline: {
|
|
SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>();
|
|
ComputePipeline* pipeline = ToBackend(cmd->pipeline).Get();
|
|
PipelineLayout* layout = ToBackend(pipeline->GetLayout());
|
|
|
|
commandList->SetComputeRootSignature(layout->GetRootSignature());
|
|
commandList->SetPipelineState(pipeline->GetPipelineState());
|
|
|
|
bindingTracker->OnSetPipeline(pipeline);
|
|
|
|
lastLayout = layout;
|
|
break;
|
|
}
|
|
|
|
case Command::SetBindGroup: {
|
|
SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
|
|
BindGroup* group = ToBackend(cmd->group.Get());
|
|
uint32_t* dynamicOffsets = nullptr;
|
|
|
|
if (cmd->dynamicOffsetCount > 0) {
|
|
dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount);
|
|
}
|
|
|
|
bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount,
|
|
dynamicOffsets);
|
|
break;
|
|
}
|
|
|
|
case Command::InsertDebugMarker: {
|
|
InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
|
|
const char* label = mCommands.NextData<char>(cmd->length + 1);
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
// PIX color is 1 byte per channel in ARGB format
|
|
constexpr uint64_t kPIXBlackColor = 0xff000000;
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::PopDebugGroup: {
|
|
mCommands.NextCommand<PopDebugGroupCmd>();
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixEndEventOnCommandList(commandList);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::PushDebugGroup: {
|
|
PushDebugGroupCmd* cmd = mCommands.NextCommand<PushDebugGroupCmd>();
|
|
const char* label = mCommands.NextData<char>(cmd->length + 1);
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
// PIX color is 1 byte per channel in ARGB format
|
|
constexpr uint64_t kPIXBlackColor = 0xff000000;
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::WriteTimestamp: {
|
|
WriteTimestampCmd* cmd = mCommands.NextCommand<WriteTimestampCmd>();
|
|
|
|
RecordWriteTimestampCmd(commandList, cmd);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError CommandBuffer::SetupRenderPass(CommandRecordingContext* commandContext,
|
|
BeginRenderPassCmd* renderPass,
|
|
RenderPassBuilder* renderPassBuilder) {
|
|
Device* device = ToBackend(GetDevice());
|
|
|
|
for (ColorAttachmentIndex i :
|
|
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
|
|
RenderPassColorAttachmentInfo& attachmentInfo = renderPass->colorAttachments[i];
|
|
TextureView* view = ToBackend(attachmentInfo.view.Get());
|
|
|
|
// Set view attachment.
|
|
CPUDescriptorHeapAllocation rtvAllocation;
|
|
DAWN_TRY_ASSIGN(
|
|
rtvAllocation,
|
|
device->GetRenderTargetViewAllocator()->AllocateTransientCPUDescriptors());
|
|
|
|
const D3D12_RENDER_TARGET_VIEW_DESC viewDesc = view->GetRTVDescriptor();
|
|
const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = rtvAllocation.GetBaseDescriptor();
|
|
|
|
device->GetD3D12Device()->CreateRenderTargetView(
|
|
ToBackend(view->GetTexture())->GetD3D12Resource(), &viewDesc, baseDescriptor);
|
|
|
|
renderPassBuilder->SetRenderTargetView(i, baseDescriptor);
|
|
|
|
// Set color load operation.
|
|
renderPassBuilder->SetRenderTargetBeginningAccess(
|
|
i, attachmentInfo.loadOp, attachmentInfo.clearColor, view->GetD3D12Format());
|
|
|
|
// Set color store operation.
|
|
if (attachmentInfo.resolveTarget != nullptr) {
|
|
TextureView* resolveDestinationView = ToBackend(attachmentInfo.resolveTarget.Get());
|
|
Texture* resolveDestinationTexture =
|
|
ToBackend(resolveDestinationView->GetTexture());
|
|
|
|
resolveDestinationTexture->TrackUsageAndTransitionNow(
|
|
commandContext, D3D12_RESOURCE_STATE_RESOLVE_DEST,
|
|
resolveDestinationView->GetSubresourceRange());
|
|
|
|
renderPassBuilder->SetRenderTargetEndingAccessResolve(i, attachmentInfo.storeOp,
|
|
view, resolveDestinationView);
|
|
} else {
|
|
renderPassBuilder->SetRenderTargetEndingAccess(i, attachmentInfo.storeOp);
|
|
}
|
|
}
|
|
|
|
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
|
|
RenderPassDepthStencilAttachmentInfo& attachmentInfo =
|
|
renderPass->depthStencilAttachment;
|
|
TextureView* view = ToBackend(renderPass->depthStencilAttachment.view.Get());
|
|
|
|
// Set depth attachment.
|
|
CPUDescriptorHeapAllocation dsvAllocation;
|
|
DAWN_TRY_ASSIGN(
|
|
dsvAllocation,
|
|
device->GetDepthStencilViewAllocator()->AllocateTransientCPUDescriptors());
|
|
|
|
const D3D12_DEPTH_STENCIL_VIEW_DESC viewDesc = view->GetDSVDescriptor();
|
|
const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = dsvAllocation.GetBaseDescriptor();
|
|
|
|
device->GetD3D12Device()->CreateDepthStencilView(
|
|
ToBackend(view->GetTexture())->GetD3D12Resource(), &viewDesc, baseDescriptor);
|
|
|
|
renderPassBuilder->SetDepthStencilView(baseDescriptor);
|
|
|
|
const bool hasDepth = view->GetTexture()->GetFormat().HasDepth();
|
|
const bool hasStencil = view->GetTexture()->GetFormat().HasStencil();
|
|
|
|
// Set depth/stencil load operations.
|
|
if (hasDepth) {
|
|
renderPassBuilder->SetDepthAccess(
|
|
attachmentInfo.depthLoadOp, attachmentInfo.depthStoreOp,
|
|
attachmentInfo.clearDepth, view->GetD3D12Format());
|
|
} else {
|
|
renderPassBuilder->SetDepthNoAccess();
|
|
}
|
|
|
|
if (hasStencil) {
|
|
renderPassBuilder->SetStencilAccess(
|
|
attachmentInfo.stencilLoadOp, attachmentInfo.stencilStoreOp,
|
|
attachmentInfo.clearStencil, view->GetD3D12Format());
|
|
} else {
|
|
renderPassBuilder->SetStencilNoAccess();
|
|
}
|
|
|
|
} else {
|
|
renderPassBuilder->SetDepthStencilNoAccess();
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
void CommandBuffer::EmulateBeginRenderPass(CommandRecordingContext* commandContext,
|
|
const RenderPassBuilder* renderPassBuilder) const {
|
|
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
|
|
|
// Clear framebuffer attachments as needed.
|
|
{
|
|
for (ColorAttachmentIndex i(uint8_t(0));
|
|
i < renderPassBuilder->GetColorAttachmentCount(); i++) {
|
|
// Load op - color
|
|
if (renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i]
|
|
.BeginningAccess.Type == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) {
|
|
commandList->ClearRenderTargetView(
|
|
renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i].cpuDescriptor,
|
|
renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i]
|
|
.BeginningAccess.Clear.ClearValue.Color,
|
|
0, nullptr);
|
|
}
|
|
}
|
|
|
|
if (renderPassBuilder->HasDepth()) {
|
|
D3D12_CLEAR_FLAGS clearFlags = {};
|
|
float depthClear = 0.0f;
|
|
uint8_t stencilClear = 0u;
|
|
|
|
if (renderPassBuilder->GetRenderPassDepthStencilDescriptor()
|
|
->DepthBeginningAccess.Type ==
|
|
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) {
|
|
clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
|
|
depthClear = renderPassBuilder->GetRenderPassDepthStencilDescriptor()
|
|
->DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth;
|
|
}
|
|
if (renderPassBuilder->GetRenderPassDepthStencilDescriptor()
|
|
->StencilBeginningAccess.Type ==
|
|
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) {
|
|
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
|
|
stencilClear =
|
|
renderPassBuilder->GetRenderPassDepthStencilDescriptor()
|
|
->StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil;
|
|
}
|
|
|
|
// TODO(kainino@chromium.org): investigate: should the Dawn clear
|
|
// stencil type be uint8_t?
|
|
if (clearFlags) {
|
|
commandList->ClearDepthStencilView(
|
|
renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor,
|
|
clearFlags, depthClear, stencilClear, 0, nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
commandList->OMSetRenderTargets(
|
|
static_cast<uint8_t>(renderPassBuilder->GetColorAttachmentCount()),
|
|
renderPassBuilder->GetRenderTargetViews(), FALSE,
|
|
renderPassBuilder->HasDepth()
|
|
? &renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor
|
|
: nullptr);
|
|
}
|
|
|
|
MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* commandContext,
|
|
BindGroupStateTracker* bindingTracker,
|
|
BeginRenderPassCmd* renderPass,
|
|
const bool passHasUAV) {
|
|
Device* device = ToBackend(GetDevice());
|
|
const bool useRenderPass = device->IsToggleEnabled(Toggle::UseD3D12RenderPass);
|
|
|
|
// renderPassBuilder must be scoped to RecordRenderPass because any underlying
|
|
// D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS structs must remain
|
|
// valid until after EndRenderPass() has been called.
|
|
RenderPassBuilder renderPassBuilder(passHasUAV);
|
|
|
|
DAWN_TRY(SetupRenderPass(commandContext, renderPass, &renderPassBuilder));
|
|
|
|
// Use D3D12's native render pass API if it's available, otherwise emulate the
|
|
// beginning and ending access operations.
|
|
if (useRenderPass) {
|
|
commandContext->GetCommandList4()->BeginRenderPass(
|
|
static_cast<uint8_t>(renderPassBuilder.GetColorAttachmentCount()),
|
|
renderPassBuilder.GetRenderPassRenderTargetDescriptors().data(),
|
|
renderPassBuilder.HasDepth()
|
|
? renderPassBuilder.GetRenderPassDepthStencilDescriptor()
|
|
: nullptr,
|
|
renderPassBuilder.GetRenderPassFlags());
|
|
} else {
|
|
EmulateBeginRenderPass(commandContext, &renderPassBuilder);
|
|
}
|
|
|
|
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
|
|
|
|
// Set up default dynamic state
|
|
{
|
|
uint32_t width = renderPass->width;
|
|
uint32_t height = renderPass->height;
|
|
D3D12_VIEWPORT viewport = {
|
|
0.f, 0.f, static_cast<float>(width), static_cast<float>(height), 0.f, 1.f};
|
|
D3D12_RECT scissorRect = {0, 0, static_cast<long>(width), static_cast<long>(height)};
|
|
commandList->RSSetViewports(1, &viewport);
|
|
commandList->RSSetScissorRects(1, &scissorRect);
|
|
|
|
static constexpr std::array<float, 4> defaultBlendFactor = {0, 0, 0, 0};
|
|
commandList->OMSetBlendFactor(&defaultBlendFactor[0]);
|
|
}
|
|
|
|
RenderPipeline* lastPipeline = nullptr;
|
|
PipelineLayout* lastLayout = nullptr;
|
|
VertexBufferTracker vertexBufferTracker = {};
|
|
|
|
auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) -> MaybeError {
|
|
switch (type) {
|
|
case Command::Draw: {
|
|
DrawCmd* draw = iter->NextCommand<DrawCmd>();
|
|
|
|
DAWN_TRY(bindingTracker->Apply(commandContext));
|
|
vertexBufferTracker.Apply(commandList, lastPipeline);
|
|
RecordFirstIndexOffset(commandList, lastPipeline, draw->firstVertex,
|
|
draw->firstInstance);
|
|
commandList->DrawInstanced(draw->vertexCount, draw->instanceCount,
|
|
draw->firstVertex, draw->firstInstance);
|
|
break;
|
|
}
|
|
|
|
case Command::DrawIndexed: {
|
|
DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>();
|
|
|
|
DAWN_TRY(bindingTracker->Apply(commandContext));
|
|
vertexBufferTracker.Apply(commandList, lastPipeline);
|
|
RecordFirstIndexOffset(commandList, lastPipeline, draw->baseVertex,
|
|
draw->firstInstance);
|
|
commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount,
|
|
draw->firstIndex, draw->baseVertex,
|
|
draw->firstInstance);
|
|
break;
|
|
}
|
|
|
|
case Command::DrawIndirect: {
|
|
DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
|
|
|
|
DAWN_TRY(bindingTracker->Apply(commandContext));
|
|
vertexBufferTracker.Apply(commandList, lastPipeline);
|
|
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
|
|
ComPtr<ID3D12CommandSignature> signature =
|
|
ToBackend(GetDevice())->GetDrawIndirectSignature();
|
|
commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource(),
|
|
draw->indirectOffset, nullptr, 0);
|
|
break;
|
|
}
|
|
|
|
case Command::DrawIndexedIndirect: {
|
|
DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>();
|
|
|
|
DAWN_TRY(bindingTracker->Apply(commandContext));
|
|
vertexBufferTracker.Apply(commandList, lastPipeline);
|
|
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
|
|
ComPtr<ID3D12CommandSignature> signature =
|
|
ToBackend(GetDevice())->GetDrawIndexedIndirectSignature();
|
|
commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource(),
|
|
draw->indirectOffset, nullptr, 0);
|
|
break;
|
|
}
|
|
|
|
case Command::InsertDebugMarker: {
|
|
InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>();
|
|
const char* label = iter->NextData<char>(cmd->length + 1);
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
// PIX color is 1 byte per channel in ARGB format
|
|
constexpr uint64_t kPIXBlackColor = 0xff000000;
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::PopDebugGroup: {
|
|
iter->NextCommand<PopDebugGroupCmd>();
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixEndEventOnCommandList(commandList);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::PushDebugGroup: {
|
|
PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>();
|
|
const char* label = iter->NextData<char>(cmd->length + 1);
|
|
|
|
if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) {
|
|
// PIX color is 1 byte per channel in ARGB format
|
|
constexpr uint64_t kPIXBlackColor = 0xff000000;
|
|
ToBackend(GetDevice())
|
|
->GetFunctions()
|
|
->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::SetRenderPipeline: {
|
|
SetRenderPipelineCmd* cmd = iter->NextCommand<SetRenderPipelineCmd>();
|
|
RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get();
|
|
PipelineLayout* layout = ToBackend(pipeline->GetLayout());
|
|
|
|
commandList->SetGraphicsRootSignature(layout->GetRootSignature());
|
|
commandList->SetPipelineState(pipeline->GetPipelineState());
|
|
commandList->IASetPrimitiveTopology(pipeline->GetD3D12PrimitiveTopology());
|
|
|
|
bindingTracker->OnSetPipeline(pipeline);
|
|
|
|
lastPipeline = pipeline;
|
|
lastLayout = layout;
|
|
break;
|
|
}
|
|
|
|
case Command::SetBindGroup: {
|
|
SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
|
|
BindGroup* group = ToBackend(cmd->group.Get());
|
|
uint32_t* dynamicOffsets = nullptr;
|
|
|
|
if (cmd->dynamicOffsetCount > 0) {
|
|
dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount);
|
|
}
|
|
|
|
bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount,
|
|
dynamicOffsets);
|
|
break;
|
|
}
|
|
|
|
case Command::SetIndexBuffer: {
|
|
SetIndexBufferCmd* cmd = iter->NextCommand<SetIndexBufferCmd>();
|
|
|
|
D3D12_INDEX_BUFFER_VIEW bufferView;
|
|
bufferView.Format = DXGIIndexFormat(cmd->format);
|
|
bufferView.BufferLocation = ToBackend(cmd->buffer)->GetVA() + cmd->offset;
|
|
bufferView.SizeInBytes = cmd->size;
|
|
|
|
commandList->IASetIndexBuffer(&bufferView);
|
|
break;
|
|
}
|
|
|
|
case Command::SetVertexBuffer: {
|
|
SetVertexBufferCmd* cmd = iter->NextCommand<SetVertexBufferCmd>();
|
|
|
|
vertexBufferTracker.OnSetVertexBuffer(cmd->slot, ToBackend(cmd->buffer.Get()),
|
|
cmd->offset, cmd->size);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
return {};
|
|
};
|
|
|
|
Command type;
|
|
while (mCommands.NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::EndRenderPass: {
|
|
mCommands.NextCommand<EndRenderPassCmd>();
|
|
if (useRenderPass) {
|
|
commandContext->GetCommandList4()->EndRenderPass();
|
|
} else if (renderPass->attachmentState->GetSampleCount() > 1) {
|
|
ResolveMultisampledRenderPass(commandContext, renderPass);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
case Command::SetStencilReference: {
|
|
SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>();
|
|
|
|
commandList->OMSetStencilRef(cmd->reference);
|
|
break;
|
|
}
|
|
|
|
case Command::SetViewport: {
|
|
SetViewportCmd* cmd = mCommands.NextCommand<SetViewportCmd>();
|
|
D3D12_VIEWPORT viewport;
|
|
viewport.TopLeftX = cmd->x;
|
|
viewport.TopLeftY = cmd->y;
|
|
viewport.Width = cmd->width;
|
|
viewport.Height = cmd->height;
|
|
viewport.MinDepth = cmd->minDepth;
|
|
viewport.MaxDepth = cmd->maxDepth;
|
|
|
|
commandList->RSSetViewports(1, &viewport);
|
|
break;
|
|
}
|
|
|
|
case Command::SetScissorRect: {
|
|
SetScissorRectCmd* cmd = mCommands.NextCommand<SetScissorRectCmd>();
|
|
D3D12_RECT rect;
|
|
rect.left = cmd->x;
|
|
rect.top = cmd->y;
|
|
rect.right = cmd->x + cmd->width;
|
|
rect.bottom = cmd->y + cmd->height;
|
|
|
|
commandList->RSSetScissorRects(1, &rect);
|
|
break;
|
|
}
|
|
|
|
case Command::SetBlendColor: {
|
|
SetBlendColorCmd* cmd = mCommands.NextCommand<SetBlendColorCmd>();
|
|
const std::array<float, 4> color = ConvertToFloatColor(cmd->color);
|
|
commandList->OMSetBlendFactor(color.data());
|
|
break;
|
|
}
|
|
|
|
case Command::ExecuteBundles: {
|
|
ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>();
|
|
auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count);
|
|
|
|
for (uint32_t i = 0; i < cmd->count; ++i) {
|
|
CommandIterator* iter = bundles[i]->GetCommands();
|
|
iter->Reset();
|
|
while (iter->NextCommandId(&type)) {
|
|
DAWN_TRY(EncodeRenderBundleCommand(iter, type));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Command::BeginOcclusionQuery: {
|
|
BeginOcclusionQueryCmd* cmd = mCommands.NextCommand<BeginOcclusionQueryCmd>();
|
|
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
|
ASSERT(D3D12QueryType(querySet->GetQueryType()) ==
|
|
D3D12_QUERY_TYPE_BINARY_OCCLUSION);
|
|
commandList->BeginQuery(querySet->GetQueryHeap(),
|
|
D3D12_QUERY_TYPE_BINARY_OCCLUSION, cmd->queryIndex);
|
|
break;
|
|
}
|
|
|
|
case Command::EndOcclusionQuery: {
|
|
EndOcclusionQueryCmd* cmd = mCommands.NextCommand<EndOcclusionQueryCmd>();
|
|
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
|
ASSERT(D3D12QueryType(querySet->GetQueryType()) ==
|
|
D3D12_QUERY_TYPE_BINARY_OCCLUSION);
|
|
commandList->EndQuery(querySet->GetQueryHeap(),
|
|
D3D12_QUERY_TYPE_BINARY_OCCLUSION, cmd->queryIndex);
|
|
break;
|
|
}
|
|
|
|
case Command::WriteTimestamp: {
|
|
WriteTimestampCmd* cmd = mCommands.NextCommand<WriteTimestampCmd>();
|
|
|
|
RecordWriteTimestampCmd(commandList, cmd);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
DAWN_TRY(EncodeRenderBundleCommand(&mCommands, type));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
}} // namespace dawn_native::d3d12
|