blob: 0def2be1c110e9f9fc8b7094a53796a183f854e1 [file] [log] [blame]
/*
* MVKCommandEncoderState.h
*
* Copyright (c) 2015-2022 The Brenwill Workshop Ltd. (http://www.brenwill.com)
*
* 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.
*/
#pragma once
#include "MVKMTLResourceBindings.h"
#include "MVKCommandResourceFactory.h"
#include "MVKDevice.h"
#include "MVKDescriptor.h"
#include "MVKSmallVector.h"
#include "MVKBitArray.h"
#include <unordered_map>
class MVKCommandEncoder;
class MVKGraphicsPipeline;
class MVKDescriptorSet;
class MVKOcclusionQueryPool;
struct MVKShaderImplicitRezBinding;
#pragma mark -
#pragma mark MVKCommandEncoderState
/**
* Abstract class that holds encoder state established by Vulkan commands.
*
* Some Vulkan commands can be issued both inside or outside a render pass, and the state
* encoded by the command needs to be retained by the encoder for use by following render
* passes. In addition, some Vulkan commands can be issued multiple times to accumulate
* encoded content that should be submitted in one shot to the Metal encoder.
*/
class MVKCommandEncoderState : public MVKBaseObject {
public:
/** Returns the Vulkan API opaque object controlling this object. */
MVKVulkanAPIObject* getVulkanAPIObject() override;
/**
* Marks the content of this instance as dirty, relative to the
* current or next Metal render pass, and in need of submission to Metal.
*/
virtual void markDirty() {
_isDirty = true;
_isModified = true;
}
/**
* Called automatically when a Metal render pass begins. If the contents have been
* modified from the default values, this instance is marked as dirty, so the contents
* will be encoded to Metal, otherwise it is marked as clean, so the contents will NOT
* be encoded. Default state can be left unencoded on a new Metal encoder.
*/
virtual void beginMetalRenderPass() { if (_isModified) { markDirty(); } }
/** Called automatically when a Metal render pass ends. */
virtual void endMetalRenderPass() { }
/**
* Called automatically when a Metal compute pass begins. If the contents have been
* modified from the default values, this instance is marked as dirty, so the contents
* will be encoded to Metal, otherwise it is marked as clean, so the contents will NOT
* be encoded. Default state can be left unencoded on a new Metal encoder.
*/
virtual void beginMetalComputeEncoding() { if (_isModified) { markDirty(); } }
/**
* If the content of this instance is dirty, marks this instance as no longer dirty
* and calls the encodeImpl() function to encode the content onto the Metal encoder.
* Subclasses must override the encodeImpl() function to do the actual work.
*/
void encode(uint32_t stage = 0) {
if ( !_isDirty ) { return; }
_isDirty = false;
encodeImpl(stage);
}
/** Constructs this instance for the specified command encoder. */
MVKCommandEncoderState(MVKCommandEncoder* cmdEncoder) : _cmdEncoder(cmdEncoder) {}
protected:
virtual void encodeImpl(uint32_t stage) = 0;
MVKDevice* getDevice();
MVKCommandEncoder* _cmdEncoder;
bool _isDirty = false;
bool _isModified = false;
};
#pragma mark -
#pragma mark MVKPipelineCommandEncoderState
/** Holds encoder state established by pipeline commands. */
class MVKPipelineCommandEncoderState : public MVKCommandEncoderState {
public:
/** Binds the pipeline. */
void bindPipeline(MVKPipeline* pipeline);
/** Returns the currently bound pipeline. */
MVKPipeline* getPipeline();
/** Constructs this instance for the specified command encoder. */
MVKPipelineCommandEncoderState(MVKCommandEncoder* cmdEncoder)
: MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
MVKPipeline* _pipeline = nullptr;
};
#pragma mark -
#pragma mark MVKViewportCommandEncoderState
/** Holds encoder state established by viewport commands. */
class MVKViewportCommandEncoderState : public MVKCommandEncoderState {
public:
/**
* Sets one or more of the viewports, starting at the first index.
* The isSettingDynamically indicates that the scissor is being changed dynamically,
* which is only allowed if the pipeline was created as VK_DYNAMIC_STATE_SCISSOR.
*/
void setViewports(const MVKArrayRef<VkViewport> viewports,
uint32_t firstViewport,
bool isSettingDynamically);
/** Constructs this instance for the specified command encoder. */
MVKViewportCommandEncoderState(MVKCommandEncoder* cmdEncoder)
: MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
MVKSmallVector<VkViewport, kMVKCachedViewportScissorCount> _viewports, _dynamicViewports;
};
#pragma mark -
#pragma mark MVKScissorCommandEncoderState
/** Holds encoder state established by viewport commands. */
class MVKScissorCommandEncoderState : public MVKCommandEncoderState {
public:
/**
* Sets one or more of the scissors, starting at the first index.
* The isSettingDynamically indicates that the scissor is being changed dynamically,
* which is only allowed if the pipeline was created as VK_DYNAMIC_STATE_SCISSOR.
*/
void setScissors(const MVKArrayRef<VkRect2D> scissors,
uint32_t firstScissor,
bool isSettingDynamically);
/** Constructs this instance for the specified command encoder. */
MVKScissorCommandEncoderState(MVKCommandEncoder* cmdEncoder)
: MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
MVKSmallVector<VkRect2D, kMVKCachedViewportScissorCount> _scissors, _dynamicScissors;
};
#pragma mark -
#pragma mark MVKPushConstantsCommandEncoderState
/** Holds encoder state established by push constant commands for a single shader stage. */
class MVKPushConstantsCommandEncoderState : public MVKCommandEncoderState {
public:
/** Sets the specified push constants. */
void setPushConstants(uint32_t offset, MVKArrayRef<char> pushConstants);
/** Sets the index of the Metal buffer used to hold the push constants. */
void setMTLBufferIndex(uint32_t mtlBufferIndex, bool pipelineStageUsesPushConstants);
/** Constructs this instance for the specified command encoder. */
MVKPushConstantsCommandEncoderState(MVKCommandEncoder* cmdEncoder,
VkShaderStageFlagBits shaderStage)
: MVKCommandEncoderState(cmdEncoder), _shaderStage(shaderStage) {}
protected:
void encodeImpl(uint32_t stage) override;
bool isTessellating();
MVKSmallVector<char, 128> _pushConstants;
VkShaderStageFlagBits _shaderStage;
uint32_t _mtlBufferIndex = 0;
bool _pipelineStageUsesPushConstants = false;
};
#pragma mark -
#pragma mark MVKDepthStencilCommandEncoderState
/** Holds encoder state established by depth stencil commands. */
class MVKDepthStencilCommandEncoderState : public MVKCommandEncoderState {
public:
/** Sets the depth stencil state during pipeline binding. */
void setDepthStencilState(const VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo);
/**
* Sets the stencil compare mask value of the indicated faces
* to the specified value, from explicit dynamic command.
*/
void setStencilCompareMask(VkStencilFaceFlags faceMask, uint32_t stencilCompareMask);
/**
* Sets the stencil write mask value of the indicated faces
* to the specified value, from explicit dynamic command.
*/
void setStencilWriteMask(VkStencilFaceFlags faceMask, uint32_t stencilWriteMask);
void beginMetalRenderPass() override;
/** Constructs this instance for the specified command encoder. */
MVKDepthStencilCommandEncoderState(MVKCommandEncoder* cmdEncoder)
: MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
void setStencilState(MVKMTLStencilDescriptorData& stencilInfo,
const VkStencilOpState& vkStencil,
bool enabled);
MVKMTLDepthStencilDescriptorData _depthStencilData = kMVKMTLDepthStencilDescriptorDataDefault;
bool _hasDepthAttachment = false;
bool _hasStencilAttachment = false;
};
#pragma mark -
#pragma mark MVKStencilReferenceValueCommandEncoderState
/** Holds encoder state established by stencil reference values commands. */
class MVKStencilReferenceValueCommandEncoderState : public MVKCommandEncoderState {
public:
/** Sets the stencil references during pipeline binding. */
void setReferenceValues(const VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo);
/** Sets the stencil state from explicit dynamic command. */
void setReferenceValues(VkStencilFaceFlags faceMask, uint32_t stencilReference);
/** Constructs this instance for the specified command encoder. */
MVKStencilReferenceValueCommandEncoderState(MVKCommandEncoder* cmdEncoder)
: MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
uint32_t _frontFaceValue = 0;
uint32_t _backFaceValue = 0;
};
#pragma mark -
#pragma mark MVKDepthBiasCommandEncoderState
/** Holds encoder state established by depth bias commands. */
class MVKDepthBiasCommandEncoderState : public MVKCommandEncoderState {
public:
/** Sets the depth bias during pipeline binding. */
void setDepthBias(const VkPipelineRasterizationStateCreateInfo& vkRasterInfo);
/** Sets the depth bias dynamically. */
void setDepthBias(float depthBiasConstantFactor,
float depthBiasSlopeFactor,
float depthBiasClamp);
/** Constructs this instance for the specified command encoder. */
MVKDepthBiasCommandEncoderState(MVKCommandEncoder* cmdEncoder)
: MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
float _depthBiasConstantFactor = 0;
float _depthBiasClamp = 0;
float _depthBiasSlopeFactor = 0;
bool _isEnabled = false;
};
#pragma mark -
#pragma mark MVKBlendColorCommandEncoderState
/** Holds encoder state established by blend color commands. */
class MVKBlendColorCommandEncoderState : public MVKCommandEncoderState {
public:
/** Sets the blend color, either as part of pipeline binding, or dynamically. */
void setBlendColor(float red, float green,
float blue, float alpha,
bool isDynamic);
/** Constructs this instance for the specified command encoder. */
MVKBlendColorCommandEncoderState(MVKCommandEncoder* cmdEncoder)
: MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
float _red = 0;
float _green = 0;
float _blue = 0;
float _alpha = 0;
};
#pragma mark -
#pragma mark MVKResourcesCommandEncoderState
/** Abstract resource state class for supporting encoder resources. */
class MVKResourcesCommandEncoderState : public MVKCommandEncoderState {
public:
/** Returns the currently bound pipeline for this bind point. */
virtual MVKPipeline* getPipeline() = 0;
/** Binds the specified descriptor set to the specified index. */
void bindDescriptorSet(uint32_t descSetIndex,
MVKDescriptorSet* descSet,
MVKShaderResourceBinding& dslMTLRezIdxOffsets,
MVKArrayRef<uint32_t> dynamicOffsets,
uint32_t& dynamicOffsetIndex);
/** Encodes the Metal resource to the Metal command encoder. */
virtual void encodeArgumentBufferResourceUsage(MVKShaderStage stage,
id<MTLResource> mtlResource,
MTLResourceUsage mtlUsage,
MTLRenderStages mtlStages) = 0;
MVKResourcesCommandEncoderState(MVKCommandEncoder* cmdEncoder) :
MVKCommandEncoderState(cmdEncoder), _boundDescriptorSets{} {}
protected:
void markDirty() override;
// Template function that marks both the vector and all binding elements in the vector as dirty.
template<class T>
void markDirty(T& bindings, bool& bindingsDirtyFlag) {
for (auto& b : bindings) { b.markDirty(); }
bindingsDirtyFlag = true;
}
// Template function that updates an existing binding or adds a new binding to a vector
// of bindings, and marks the binding, the vector, and this instance as dirty
template<class T, class V>
void bind(const T& b, V& bindings, bool& bindingsDirtyFlag) {
if ( !b.mtlResource ) { return; }
MVKCommandEncoderState::markDirty();
bindingsDirtyFlag = true;
for (auto iter = bindings.begin(), end = bindings.end(); iter != end; ++iter) {
if (iter->index == b.index) {
iter->update(b);
return;
}
}
bindings.push_back(b);
bindings.back().markDirty();
}
// For texture bindings, we also keep track of whether any bindings need a texture swizzle
template<class V>
void bind(const MVKMTLTextureBinding& tb, V& texBindings, bool& bindingsDirtyFlag, bool& needsSwizzleFlag) {
bind(tb, texBindings, bindingsDirtyFlag);
if (tb.swizzle != 0) { needsSwizzleFlag = true; }
}
// Template function that executes a lambda expression on each dirty element of
// a vector of bindings, and marks the bindings and the vector as no longer dirty.
// Clear isDirty flag before operation to allow operation to possibly override.
template<class T, class V>
void encodeBinding(V& bindings,
bool& bindingsDirtyFlag,
std::function<void(MVKCommandEncoder* cmdEncoder, T& b)> mtlOperation) {
if (bindingsDirtyFlag) {
bindingsDirtyFlag = false;
for (auto& b : bindings) {
if (b.isDirty) {
b.isDirty = false;
mtlOperation(_cmdEncoder, b);
if (b.isDirty) { bindingsDirtyFlag = true; }
}
}
}
}
// Updates a value at the given index in the given vector, resizing if needed.
template<class V>
void updateImplicitBuffer(V &contents, uint32_t index, uint32_t value) {
if (index >= contents.size()) { contents.resize(index + 1); }
contents[index] = value;
}
void assertMissingSwizzles(bool needsSwizzle, const char* stageName, const MVKArrayRef<MVKMTLTextureBinding> texBindings);
void encodeMetalArgumentBuffer(MVKShaderStage stage);
virtual void bindMetalArgumentBuffer(MVKShaderStage stage, MVKMTLBufferBinding& buffBind) = 0;
template<size_t N>
struct ResourceBindings {
MVKSmallVector<MVKMTLBufferBinding, N> bufferBindings;
MVKSmallVector<MVKMTLTextureBinding, N> textureBindings;
MVKSmallVector<MVKMTLSamplerStateBinding, N> samplerStateBindings;
MVKSmallVector<uint32_t, N> swizzleConstants;
MVKSmallVector<uint32_t, N> bufferSizes;
MVKMTLBufferBinding swizzleBufferBinding;
MVKMTLBufferBinding bufferSizeBufferBinding;
MVKMTLBufferBinding dynamicOffsetBufferBinding;
MVKMTLBufferBinding viewRangeBufferBinding;
bool areBufferBindingsDirty = false;
bool areTextureBindingsDirty = false;
bool areSamplerStateBindingsDirty = false;
bool needsSwizzle = false;
};
MVKDescriptorSet* _boundDescriptorSets[kMVKMaxDescriptorSetCount];
MVKBitArray _metalUsageDirtyDescriptors[kMVKMaxDescriptorSetCount];
MVKSmallVector<uint32_t, 8> _dynamicOffsets;
};
#pragma mark -
#pragma mark MVKGraphicsResourcesCommandEncoderState
/** Holds graphics encoder resource state established by bind vertex buffer and descriptor set commands. */
class MVKGraphicsResourcesCommandEncoderState : public MVKResourcesCommandEncoderState {
public:
/** Returns the currently bound pipeline for this bind point. */
MVKPipeline* getPipeline() override;
/** Binds the specified buffer for the specified shader stage. */
void bindBuffer(MVKShaderStage stage, const MVKMTLBufferBinding& binding);
/** Binds the specified texture for the specified shader stage. */
void bindTexture(MVKShaderStage stage, const MVKMTLTextureBinding& binding);
/** Binds the specified sampler state for the specified shader stage. */
void bindSamplerState(MVKShaderStage stage, const MVKMTLSamplerStateBinding& binding);
/** The type of index that will be used to render primitives. Exposed directly. */
MVKIndexMTLBufferBinding _mtlIndexBufferBinding;
/** Binds the specified index buffer. */
void bindIndexBuffer(const MVKIndexMTLBufferBinding& binding) {
_mtlIndexBufferBinding = binding; // No need to track dirty state
}
/** Sets the current swizzle buffer state. */
void bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding,
bool needVertexSwizzleBuffer,
bool needTessCtlSwizzleBuffer,
bool needTessEvalSwizzleBuffer,
bool needFragmentSwizzleBuffer);
/** Sets the current buffer size buffer state. */
void bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding,
bool needVertexSizeBuffer,
bool needTessCtlSizeBuffer,
bool needTessEvalSizeBuffer,
bool needFragmentSizeBuffer);
/** Sets the current dynamic offset buffer state. */
void bindDynamicOffsetBuffer(const MVKShaderImplicitRezBinding& binding,
bool needVertexDynanicOffsetBuffer,
bool needTessCtlDynanicOffsetBuffer,
bool needTessEvalDynanicOffsetBuffer,
bool needFragmentDynanicOffsetBuffer);
/** Sets the current view range buffer state. */
void bindViewRangeBuffer(const MVKShaderImplicitRezBinding& binding,
bool needVertexViewBuffer,
bool needFragmentViewBuffer);
void encodeBindings(MVKShaderStage stage,
const char* pStageName,
bool fullImageViewSwizzle,
std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&)> bindBuffer,
std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&, const MVKArrayRef<uint32_t>)> bindImplicitBuffer,
std::function<void(MVKCommandEncoder*, MVKMTLTextureBinding&)> bindTexture,
std::function<void(MVKCommandEncoder*, MVKMTLSamplerStateBinding&)> bindSampler);
void encodeArgumentBufferResourceUsage(MVKShaderStage stage,
id<MTLResource> mtlResource,
MTLResourceUsage mtlUsage,
MTLRenderStages mtlStages) override;
/** Offset all buffers for vertex attribute bindings with zero divisors by the given number of strides. */
void offsetZeroDivisorVertexBuffers(MVKGraphicsStage stage, MVKGraphicsPipeline* pipeline, uint32_t firstInstance);
#pragma mark Construction
/** Constructs this instance for the specified command encoder. */
MVKGraphicsResourcesCommandEncoderState(MVKCommandEncoder* cmdEncoder) : MVKResourcesCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t stage) override;
void markDirty() override;
void bindMetalArgumentBuffer(MVKShaderStage stage, MVKMTLBufferBinding& buffBind) override;
ResourceBindings<8> _shaderStageResourceBindings[4];
};
#pragma mark -
#pragma mark MVKComputeResourcesCommandEncoderState
/** Holds compute encoder resource state established by bind vertex buffer and descriptor set commands. */
class MVKComputeResourcesCommandEncoderState : public MVKResourcesCommandEncoderState {
public:
/** Returns the currently bound pipeline for this bind point. */
MVKPipeline* getPipeline() override;
/** Binds the specified buffer. */
void bindBuffer(const MVKMTLBufferBinding& binding);
/** Binds the specified texture. */
void bindTexture(const MVKMTLTextureBinding& binding);
/** Binds the specified sampler state. */
void bindSamplerState(const MVKMTLSamplerStateBinding& binding);
/** Sets the current swizzle buffer state. */
void bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding, bool needSwizzleBuffer);
/** Sets the current buffer size buffer state. */
void bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding, bool needSizeBuffer);
/** Sets the current dynamic offset buffer state. */
void bindDynamicOffsetBuffer(const MVKShaderImplicitRezBinding& binding, bool needDynamicOffsetBuffer);
void encodeArgumentBufferResourceUsage(MVKShaderStage stage,
id<MTLResource> mtlResource,
MTLResourceUsage mtlUsage,
MTLRenderStages mtlStages) override;
void markDirty() override;
#pragma mark Construction
/** Constructs this instance for the specified command encoder. */
MVKComputeResourcesCommandEncoderState(MVKCommandEncoder* cmdEncoder) : MVKResourcesCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t) override;
void bindMetalArgumentBuffer(MVKShaderStage stage, MVKMTLBufferBinding& buffBind) override;
ResourceBindings<4> _resourceBindings;
};
#pragma mark -
#pragma mark MVKOcclusionQueryCommandEncoderState
/** Holds encoder state established by occlusion query commands. */
class MVKOcclusionQueryCommandEncoderState : public MVKCommandEncoderState {
public:
void endMetalRenderPass() override;
/** Begins an occlusion query. */
void beginOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query, VkQueryControlFlags flags);
/** Ends an occlusion query. */
void endOcclusionQuery(MVKOcclusionQueryPool* pQueryPool, uint32_t query);
MVKOcclusionQueryCommandEncoderState(MVKCommandEncoder* cmdEncoder) : MVKCommandEncoderState(cmdEncoder) {}
protected:
void encodeImpl(uint32_t) override;
typedef struct OcclusionQueryLocation {
MVKOcclusionQueryPool* queryPool = nullptr;
uint32_t query = 0;
NSUInteger visibilityBufferOffset = 0;
OcclusionQueryLocation(MVKOcclusionQueryPool* qPool, uint32_t qIdx, NSUInteger vbOfst)
: queryPool(qPool), query(qIdx), visibilityBufferOffset(vbOfst) {}
} OcclusionQueryLocation;
MVKSmallVector<OcclusionQueryLocation> _mtlRenderPassQueries;
MTLVisibilityResultMode _mtlVisibilityResultMode = MTLVisibilityResultModeDisabled;
};