Commit 113c5e29 by Le Hoang Quyen Committed by Commit Bot

Metal: deferred render command encoder creation.

MTLRenderCommandEncoder creation will be deferred until a render pass ends. Commands will be stored into a back-end owned buffer during render pass. At the end of the render pass, those commands will be re-encoded into an actual MTLRenderCommandEncoder. Benefits: - Useful for future implementation of occlusion query buffer where it could be allocated right before the end of a render pass to be big enough to store all queries within the render pass. - It's possible to change load option (deferred clear) as long as there is no draw call issued yet. This is not implemented yet. - Possibility of commands' re-ordering. Bug: angleproject:2634 Change-Id: I1348716aa882c0540d9120bf175d8dac13fb58bd Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2193196 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 94e8a3d9
......@@ -285,7 +285,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
mtl::ComputeCommandEncoder *getComputeCommandEncoder();
private:
void ensureCommandBufferValid();
void ensureCommandBufferReady();
angle::Result ensureIncompleteTexturesCreated(const gl::Context *context);
angle::Result setupDraw(const gl::Context *context,
gl::PrimitiveMode mode,
......
......@@ -1087,7 +1087,7 @@ void ContextMtl::endEncoding(bool forceSaveRenderPassContent)
void ContextMtl::flushCommandBufer()
{
if (!mCmdBuffer.valid())
if (!mCmdBuffer.ready())
{
return;
}
......@@ -1098,7 +1098,7 @@ void ContextMtl::flushCommandBufer()
void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> presentationDrawable)
{
ensureCommandBufferValid();
ensureCommandBufferReady();
// Always discard default FBO's depth stencil buffers at the end of the frame:
if (mDrawFramebufferIsDefault && hasStartedRenderPass(mDrawFramebuffer))
......@@ -1121,10 +1121,7 @@ angle::Result ContextMtl::finishCommandBuffer()
{
flushCommandBufer();
if (mCmdBuffer.valid())
{
mCmdBuffer.finish();
}
mCmdBuffer.finish();
return angle::Result::Continue;
}
......@@ -1170,7 +1167,7 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::Render
endEncoding(false);
ensureCommandBufferValid();
ensureCommandBufferReady();
// Need to re-apply everything on next draw call.
mDirtyBits.set();
......@@ -1219,7 +1216,7 @@ mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoder()
endEncoding(true);
ensureCommandBufferValid();
ensureCommandBufferReady();
return &mBlitEncoder.restart();
}
......@@ -1233,19 +1230,19 @@ mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoder()
endEncoding(true);
ensureCommandBufferValid();
ensureCommandBufferReady();
return &mComputeEncoder.restart();
}
void ContextMtl::ensureCommandBufferValid()
void ContextMtl::ensureCommandBufferReady()
{
if (!mCmdBuffer.valid())
if (!mCmdBuffer.ready())
{
mCmdBuffer.restart();
}
ASSERT(mCmdBuffer.valid());
ASSERT(mCmdBuffer.ready());
}
void ContextMtl::updateViewport(FramebufferMtl *framebufferMtl,
......
......@@ -693,21 +693,8 @@ angle::Result ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommand
{
continue;
}
switch (shaderType)
{
case gl::ShaderType::Vertex:
cmdEncoder->setVertexBytes(uniformBlock.uniformData.data(),
uniformBlock.uniformData.size(),
mtl::kDefaultUniformsBindingIndex);
break;
case gl::ShaderType::Fragment:
cmdEncoder->setFragmentBytes(uniformBlock.uniformData.data(),
uniformBlock.uniformData.size(),
mtl::kDefaultUniformsBindingIndex);
break;
default:
UNREACHABLE();
}
cmdEncoder->setBytes(shaderType, uniformBlock.uniformData.data(),
uniformBlock.uniformData.size(), mtl::kDefaultUniformsBindingIndex);
mDefaultUniformBlocksDirty.reset(shaderType);
}
......@@ -762,19 +749,8 @@ angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
TextureMtl *textureMtl = mtl::GetImpl(texture);
switch (shaderType)
{
case gl::ShaderType::Vertex:
ANGLE_TRY(textureMtl->bindVertexShader(glContext, cmdEncoder, textureSlot,
samplerSlot));
break;
case gl::ShaderType::Fragment:
ANGLE_TRY(textureMtl->bindFragmentShader(glContext, cmdEncoder, textureSlot,
samplerSlot));
break;
default:
UNREACHABLE();
}
ANGLE_TRY(textureMtl->bindToShader(glContext, cmdEncoder, shaderType, textureSlot,
samplerSlot));
} // for array elements
} // for sampler bindings
} // for shader types
......
......@@ -150,14 +150,11 @@ class TextureMtl : public TextureImpl
// to the actual texture.
angle::Result ensureTextureCreated(const gl::Context *context);
angle::Result bindVertexShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex,
int samplerSlotIndex);
angle::Result bindFragmentShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex,
int samplerSlotIndex);
angle::Result bindToShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
gl::ShaderType shaderType,
int textureSlotIndex,
int samplerSlotIndex);
const mtl::Format &getFormat() const { return mFormat; }
......
......@@ -418,8 +418,8 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
{
encoder = contextMtl->getBlitCommandEncoder();
}
encoder->copyTexture(mNativeTexture, layer, mip, mtlOrigin, mtlSize,
imageToTransfer, 0, 0, mtlOrigin);
encoder->copyTexture(imageToTransfer, 0, 0, mtlOrigin, mtlSize, mNativeTexture,
layer, mip, mtlOrigin);
}
imageToTransfer = nullptr;
......@@ -828,42 +828,18 @@ angle::Result TextureMtl::syncState(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result TextureMtl::bindVertexShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex,
int samplerSlotIndex)
angle::Result TextureMtl::bindToShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
gl::ShaderType shaderType,
int textureSlotIndex,
int samplerSlotIndex)
{
ASSERT(mNativeTexture);
// ES 2.0: non power of two texture won't have any mipmap.
// We don't support OES_texture_npot atm.
float maxLodClamp = FLT_MAX;
if (!mIsPow2)
{
maxLodClamp = 0;
}
cmdEncoder->setVertexTexture(mNativeTexture, textureSlotIndex);
cmdEncoder->setVertexSamplerState(mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex);
return angle::Result::Continue;
}
angle::Result TextureMtl::bindFragmentShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex,
int samplerSlotIndex)
{
ASSERT(mNativeTexture);
// ES 2.0: non power of two texture won't have any mipmap.
// We don't support OES_texture_npot atm.
float maxLodClamp = FLT_MAX;
if (!mIsPow2)
{
maxLodClamp = 0;
}
cmdEncoder->setFragmentTexture(mNativeTexture, textureSlotIndex);
cmdEncoder->setFragmentSamplerState(mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex);
cmdEncoder->setTexture(shaderType, mNativeTexture, textureSlotIndex);
cmdEncoder->setSamplerState(shaderType, mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex);
return angle::Result::Continue;
}
......
......@@ -59,6 +59,9 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non
}
bool isResourceBeingUsedByGPU(const Resource *resource) const;
// Checks whether the last command buffer that uses the given resource has been committed or not
bool resourceHasPendingWorks(const Resource *resource) const;
CommandQueue &operator=(id<MTLCommandQueue> metalQueue)
{
set(metalQueue);
......@@ -66,6 +69,7 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non
}
AutoObjCPtr<id<MTLCommandBuffer>> makeMetalCommandBuffer(uint64_t *queueSerialOut);
void onCommandBufferCommitted(id<MTLCommandBuffer> buf, uint64_t serial);
private:
void onCommandBufferCompleted(id<MTLCommandBuffer> buf, uint64_t serial);
......@@ -80,6 +84,7 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non
std::deque<CmdBufferQueueEntry> mMetalCmdBuffersTmp;
uint64_t mQueueSerialCounter = 1;
std::atomic<uint64_t> mCommittedBufferSerial{0};
std::atomic<uint64_t> mCompletedBufferSerial{0};
mutable std::mutex mLock;
......@@ -91,10 +96,14 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
CommandBuffer(CommandQueue *cmdQueue);
~CommandBuffer();
// This method must be called so that command encoder can be used.
void restart();
bool valid() const;
// Return true if command buffer can be encoded into. Return false if it has been committed
// and hasn't been restarted.
bool ready() const;
void commit();
// wait for committed command buffer to finish.
void finish();
void present(id<CAMetalDrawable> presentationDrawable);
......@@ -104,6 +113,7 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
CommandQueue &cmdQueue() { return mCmdQueue; }
// Private use only
void setActiveCommandEncoder(CommandEncoder *encoder);
void invalidateActiveCommandEncoder(CommandEncoder *encoder);
......@@ -111,14 +121,15 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
void set(id<MTLCommandBuffer> metalBuffer);
void cleanup();
bool validImpl() const;
bool readyImpl() const;
void commitImpl();
void forceEndingCurrentEncoder();
using ParentClass = WrappedObject<id<MTLCommandBuffer>>;
CommandQueue &mCmdQueue;
std::atomic<CommandEncoder *> mActiveCommandEncoder{nullptr};
CommandEncoder *mActiveCommandEncoder = nullptr;
uint64_t mQueueSerial = 0;
......@@ -141,7 +152,7 @@ class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>>, angle::NonCo
virtual void endEncoding();
void reset();
virtual void reset();
Type getType() const { return mType; }
CommandEncoder &markResourceBeingWrittenByGPU(const BufferRef &buffer);
......@@ -162,14 +173,123 @@ class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>>, angle::NonCo
CommandBuffer &mCmdBuffer;
};
// Stream to store commands before encoding them into the real MTLCommandEncoder
class IntermediateCommandStream
{
public:
template <typename T>
inline IntermediateCommandStream &push(const T &val)
{
const uint8_t *ptr = reinterpret_cast<const uint8_t *>(&val);
mBuffer.insert(mBuffer.end(), ptr, ptr + sizeof(T));
return *this;
}
inline IntermediateCommandStream &push(const uint8_t *bytes, size_t len)
{
mBuffer.insert(mBuffer.end(), bytes, bytes + len);
return *this;
}
template <typename T>
inline T peek()
{
ASSERT(mReadPtr <= mBuffer.size() - sizeof(T));
T re;
uint8_t *ptr = reinterpret_cast<uint8_t *>(&re);
std::copy(mBuffer.data() + mReadPtr, mBuffer.data() + mReadPtr + sizeof(T), ptr);
return re;
}
template <typename T>
inline T fetch()
{
T re = peek<T>();
mReadPtr += sizeof(T);
return re;
}
inline const uint8_t *fetch(size_t bytes)
{
ASSERT(mReadPtr <= mBuffer.size() - bytes);
size_t cur = mReadPtr;
mReadPtr += bytes;
return mBuffer.data() + cur;
}
inline void clear()
{
mBuffer.clear();
mReadPtr = 0;
}
inline void resetReadPtr(size_t readPtr)
{
ASSERT(readPtr <= mBuffer.size());
mReadPtr = readPtr;
}
inline bool good() const { return mReadPtr < mBuffer.size(); }
private:
std::vector<uint8_t> mBuffer;
size_t mReadPtr = 0;
};
// Per shader stage's states
struct RenderCommandEncoderShaderStates
{
RenderCommandEncoderShaderStates();
void reset();
std::array<id<MTLBuffer>, kMaxShaderBuffers> buffers;
std::array<uint32_t, kMaxShaderBuffers> bufferOffsets;
std::array<id<MTLSamplerState>, kMaxShaderSamplers> samplers;
std::array<Optional<std::pair<float, float>>, kMaxShaderSamplers> samplerLodClamps;
std::array<id<MTLTexture>, kMaxShaderSamplers> textures;
};
// Per render pass's states
struct RenderCommandEncoderStates
{
RenderCommandEncoderStates();
void reset();
id<MTLRenderPipelineState> renderPipeline;
MTLTriangleFillMode triangleFillMode;
MTLWinding winding;
MTLCullMode cullMode;
id<MTLDepthStencilState> depthStencilState;
float depthBias, depthSlopeScale, depthClamp;
uint32_t stencilFrontRef, stencilBackRef;
Optional<MTLViewport> viewport;
Optional<MTLScissorRect> scissorRect;
std::array<float, 4> blendColor;
gl::ShaderMap<RenderCommandEncoderShaderStates> perShaderStates;
};
// Encoder for encoding render commands
class RenderCommandEncoder final : public CommandEncoder
{
public:
RenderCommandEncoder(CommandBuffer *cmdBuffer);
~RenderCommandEncoder() override;
// override CommandEncoder
bool valid() const { return mRecording; }
void reset() override;
void endEncoding() override;
// Restart the encoder so that new commands can be encoded.
// NOTE: parent CommandBuffer's restart() must be called before this.
RenderCommandEncoder &restart(const RenderPassDesc &desc);
RenderCommandEncoder &setRenderPipelineState(id<MTLRenderPipelineState> state);
......@@ -187,8 +307,14 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setBlendColor(float r, float g, float b, float a);
RenderCommandEncoder &setVertexBuffer(const BufferRef &buffer, uint32_t offset, uint32_t index);
RenderCommandEncoder &setVertexBytes(const uint8_t *bytes, size_t size, uint32_t index);
RenderCommandEncoder &setVertexBuffer(const BufferRef &buffer, uint32_t offset, uint32_t index)
{
return setBuffer(gl::ShaderType::Vertex, buffer, offset, index);
}
RenderCommandEncoder &setVertexBytes(const uint8_t *bytes, size_t size, uint32_t index)
{
return setBytes(gl::ShaderType::Vertex, bytes, size, index);
}
template <typename T>
RenderCommandEncoder &setVertexData(const T &data, uint32_t index)
{
......@@ -197,13 +323,25 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setVertexSamplerState(id<MTLSamplerState> state,
float lodMinClamp,
float lodMaxClamp,
uint32_t index);
RenderCommandEncoder &setVertexTexture(const TextureRef &texture, uint32_t index);
uint32_t index)
{
return setSamplerState(gl::ShaderType::Vertex, state, lodMinClamp, lodMaxClamp, index);
}
RenderCommandEncoder &setVertexTexture(const TextureRef &texture, uint32_t index)
{
return setTexture(gl::ShaderType::Vertex, texture, index);
}
RenderCommandEncoder &setFragmentBuffer(const BufferRef &buffer,
uint32_t offset,
uint32_t index);
RenderCommandEncoder &setFragmentBytes(const uint8_t *bytes, size_t size, uint32_t index);
uint32_t index)
{
return setBuffer(gl::ShaderType::Fragment, buffer, offset, index);
}
RenderCommandEncoder &setFragmentBytes(const uint8_t *bytes, size_t size, uint32_t index)
{
return setBytes(gl::ShaderType::Fragment, bytes, size, index);
}
template <typename T>
RenderCommandEncoder &setFragmentData(const T &data, uint32_t index)
{
......@@ -212,8 +350,40 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setFragmentSamplerState(id<MTLSamplerState> state,
float lodMinClamp,
float lodMaxClamp,
uint32_t index);
RenderCommandEncoder &setFragmentTexture(const TextureRef &texture, uint32_t index);
uint32_t index)
{
return setSamplerState(gl::ShaderType::Fragment, state, lodMinClamp, lodMaxClamp, index);
}
RenderCommandEncoder &setFragmentTexture(const TextureRef &texture, uint32_t index)
{
return setTexture(gl::ShaderType::Fragment, texture, index);
}
RenderCommandEncoder &setBuffer(gl::ShaderType shaderType,
const BufferRef &buffer,
uint32_t offset,
uint32_t index);
RenderCommandEncoder &setBufferForWrite(gl::ShaderType shaderType,
const BufferRef &buffer,
uint32_t offset,
uint32_t index);
RenderCommandEncoder &setBytes(gl::ShaderType shaderType,
const uint8_t *bytes,
size_t size,
uint32_t index);
template <typename T>
RenderCommandEncoder &setData(gl::ShaderType shaderType, const T &data, uint32_t index)
{
return setBytes(shaderType, reinterpret_cast<const uint8_t *>(&data), sizeof(T), index);
}
RenderCommandEncoder &setSamplerState(gl::ShaderType shaderType,
id<MTLSamplerState> state,
float lodMinClamp,
float lodMaxClamp,
uint32_t index);
RenderCommandEncoder &setTexture(gl::ShaderType shaderType,
const TextureRef &texture,
uint32_t index);
RenderCommandEncoder &draw(MTLPrimitiveType primitiveType,
uint32_t vertexStart,
......@@ -250,20 +420,51 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setDepthStoreAction(MTLStoreAction action);
RenderCommandEncoder &setStencilStoreAction(MTLStoreAction action);
// Change the render pass's loadAction. Note that this operation is only allowed when there
// is no draw call recorded yet.
RenderCommandEncoder &setColorLoadAction(MTLLoadAction action,
const MTLClearColor &clearValue,
uint32_t colorAttachmentIndex);
RenderCommandEncoder &setDepthLoadAction(MTLLoadAction action, double clearValue);
RenderCommandEncoder &setStencilLoadAction(MTLLoadAction action, uint32_t clearValue);
const RenderPassDesc &renderPassDesc() const { return mRenderPassDesc; }
bool hasDrawCalls() const { return mHasDrawCalls; }
private:
// Override CommandEncoder
id<MTLRenderCommandEncoder> get()
{
return static_cast<id<MTLRenderCommandEncoder>>(CommandEncoder::get());
}
inline void initWriteDependencyAndStoreAction(const TextureRef &texture,
MTLStoreAction *storeActionOut);
void initAttachmentWriteDependencyAndScissorRect(const RenderPassAttachmentDesc &attachment);
void finalizeLoadStoreAction(MTLRenderPassAttachmentDescriptor *objCRenderPassAttachment);
void encodeMetalEncoder();
RenderCommandEncoder &commonSetBuffer(gl::ShaderType shaderType,
id<MTLBuffer> mtlBuffer,
uint32_t offset,
uint32_t index);
RenderPassDesc mRenderPassDesc;
MTLStoreAction mColorInitialStoreActions[kMaxRenderTargets];
MTLStoreAction mDepthInitialStoreAction;
MTLStoreAction mStencilInitialStoreAction;
// Cached Objective-C render pass desc to avoid re-allocate every frame.
mtl::AutoObjCObj<MTLRenderPassDescriptor> mCachedRenderPassDescObjC;
MTLScissorRect mRenderPassMaxScissorRect;
bool mRecording = false;
bool mHasDrawCalls = false;
IntermediateCommandStream mCommands;
gl::ShaderMap<uint8_t> mSetBufferCmds;
gl::ShaderMap<uint8_t> mSetBufferOffsetCmds;
gl::ShaderMap<uint8_t> mSetBytesCmds;
gl::ShaderMap<uint8_t> mSetTextureCmds;
gl::ShaderMap<uint8_t> mSetSamplerCmds;
RenderCommandEncoderStates mStateCache = {};
};
class BlitCommandEncoder final : public CommandEncoder
......@@ -272,6 +473,8 @@ class BlitCommandEncoder final : public CommandEncoder
BlitCommandEncoder(CommandBuffer *cmdBuffer);
~BlitCommandEncoder() override;
// Restart the encoder so that new commands can be encoded.
// NOTE: parent CommandBuffer's restart() must be called before this.
BlitCommandEncoder &restart();
BlitCommandEncoder &copyBufferToTexture(const BufferRef &src,
......@@ -285,15 +488,15 @@ class BlitCommandEncoder final : public CommandEncoder
MTLOrigin dstOrigin,
MTLBlitOption blitOption);
BlitCommandEncoder &copyTexture(const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin,
MTLSize dstSize,
const TextureRef &src,
BlitCommandEncoder &copyTexture(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
MTLOrigin srcOrigin);
MTLOrigin srcOrigin,
MTLSize srcSize,
const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin);
BlitCommandEncoder &generateMipmapsForTexture(const TextureRef &texture);
BlitCommandEncoder &synchronizeResource(const TextureRef &texture);
......@@ -311,11 +514,16 @@ class ComputeCommandEncoder final : public CommandEncoder
ComputeCommandEncoder(CommandBuffer *cmdBuffer);
~ComputeCommandEncoder() override;
// Restart the encoder so that new commands can be encoded.
// NOTE: parent CommandBuffer's restart() must be called before this.
ComputeCommandEncoder &restart();
ComputeCommandEncoder &setComputePipelineState(id<MTLComputePipelineState> state);
ComputeCommandEncoder &setBuffer(const BufferRef &buffer, uint32_t offset, uint32_t index);
ComputeCommandEncoder &setBufferForWrite(const BufferRef &buffer,
uint32_t offset,
uint32_t index);
ComputeCommandEncoder &setBytes(const uint8_t *bytes, size_t size, uint32_t index);
template <typename T>
ComputeCommandEncoder &setData(const T &data, uint32_t index)
......@@ -327,10 +535,13 @@ class ComputeCommandEncoder final : public CommandEncoder
float lodMaxClamp,
uint32_t index);
ComputeCommandEncoder &setTexture(const TextureRef &texture, uint32_t index);
ComputeCommandEncoder &setTextureForWrite(const TextureRef &texture, uint32_t index);
ComputeCommandEncoder &dispatch(MTLSize threadGroupsPerGrid, MTLSize threadsPerGroup);
ComputeCommandEncoder &dispatch(const MTLSize &threadGroupsPerGrid,
const MTLSize &threadsPerGroup);
ComputeCommandEncoder &dispatchNonUniform(MTLSize threadsPerGrid, MTLSize threadsPerGroup);
ComputeCommandEncoder &dispatchNonUniform(const MTLSize &threadsPerGrid,
const MTLSize &threadsPerGroup);
private:
id<MTLComputeCommandEncoder> get()
......
......@@ -15,11 +15,314 @@
#include "common/debug.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
// Use to compare the new values with the values already set in the command encoder:
static inline bool operator==(const MTLViewport &lhs, const MTLViewport &rhs)
{
return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
}
static inline bool operator==(const MTLScissorRect &lhs, const MTLScissorRect &rhs)
{
return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
}
namespace rx
{
namespace mtl
{
namespace
{
#define ANGLE_MTL_CMD_X(PROC) \
PROC(Invalid) \
PROC(SetRenderPipelineState) \
PROC(SetTriangleFillMode) \
PROC(SetFrontFacingWinding) \
PROC(SetCullMode) \
PROC(SetDepthStencilState) \
PROC(SetDepthBias) \
PROC(SetStencilRefVals) \
PROC(SetViewport) \
PROC(SetScissorRect) \
PROC(SetBlendColor) \
PROC(SetVertexBuffer) \
PROC(SetVertexBufferOffset) \
PROC(SetVertexBytes) \
PROC(SetVertexSamplerState) \
PROC(SetVertexTexture) \
PROC(SetFragmentBuffer) \
PROC(SetFragmentBufferOffset) \
PROC(SetFragmentBytes) \
PROC(SetFragmentSamplerState) \
PROC(SetFragmentTexture) \
PROC(Draw) \
PROC(DrawInstanced) \
PROC(DrawIndexed) \
PROC(DrawIndexedInstanced) \
PROC(DrawIndexedInstancedBaseVertex)
#define ANGLE_MTL_TYPE_DECL(CMD) CMD,
// Command types
enum class CmdType : uint8_t
{
ANGLE_MTL_CMD_X(ANGLE_MTL_TYPE_DECL)
};
// Commands decoder
void InvalidCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
UNREACHABLE();
}
void SetRenderPipelineStateCmd(id<MTLRenderCommandEncoder> encoder,
IntermediateCommandStream *stream)
{
id<MTLRenderPipelineState> state = stream->fetch<id<MTLRenderPipelineState>>();
[encoder setRenderPipelineState:state];
[state ANGLE_MTL_RELEASE];
}
void SetTriangleFillModeCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLTriangleFillMode mode = stream->fetch<MTLTriangleFillMode>();
[encoder setTriangleFillMode:mode];
}
void SetFrontFacingWindingCmd(id<MTLRenderCommandEncoder> encoder,
IntermediateCommandStream *stream)
{
MTLWinding winding = stream->fetch<MTLWinding>();
[encoder setFrontFacingWinding:winding];
}
void SetCullModeCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLCullMode mode = stream->fetch<MTLCullMode>();
[encoder setCullMode:mode];
}
void SetDepthStencilStateCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
id<MTLDepthStencilState> state = stream->fetch<id<MTLDepthStencilState>>();
[encoder setDepthStencilState:state];
[state ANGLE_MTL_RELEASE];
}
void SetDepthBiasCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
float depthBias = stream->fetch<float>();
float slopeScale = stream->fetch<float>();
float clamp = stream->fetch<float>();
[encoder setDepthBias:depthBias slopeScale:slopeScale clamp:clamp];
}
void SetStencilRefValsCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
// Metal has some bugs when reference values are larger than 0xff
uint32_t frontRef = stream->fetch<uint32_t>();
uint32_t backRef = stream->fetch<uint32_t>();
[encoder setStencilFrontReferenceValue:frontRef backReferenceValue:backRef];
}
void SetViewportCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLViewport viewport = stream->fetch<MTLViewport>();
[encoder setViewport:viewport];
}
void SetScissorRectCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLScissorRect rect = stream->fetch<MTLScissorRect>();
[encoder setScissorRect:rect];
}
void SetBlendColorCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
float r = stream->fetch<float>();
float g = stream->fetch<float>();
float b = stream->fetch<float>();
float a = stream->fetch<float>();
[encoder setBlendColorRed:r green:g blue:b alpha:a];
}
void SetVertexBufferCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
id<MTLBuffer> buffer = stream->fetch<id<MTLBuffer>>();
uint32_t offset = stream->fetch<uint32_t>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setVertexBuffer:buffer offset:offset atIndex:index];
[buffer ANGLE_MTL_RELEASE];
}
void SetVertexBufferOffsetCmd(id<MTLRenderCommandEncoder> encoder,
IntermediateCommandStream *stream)
{
uint32_t offset = stream->fetch<uint32_t>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setVertexBufferOffset:offset atIndex:index];
}
void SetVertexBytesCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
size_t size = stream->fetch<size_t>();
const uint8_t *bytes = stream->fetch(size);
uint32_t index = stream->fetch<uint32_t>();
[encoder setVertexBytes:bytes length:size atIndex:index];
}
void SetVertexSamplerStateCmd(id<MTLRenderCommandEncoder> encoder,
IntermediateCommandStream *stream)
{
id<MTLSamplerState> state = stream->fetch<id<MTLSamplerState>>();
float lodMinClamp = stream->fetch<float>();
float lodMaxClamp = stream->fetch<float>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setVertexSamplerState:state
lodMinClamp:lodMinClamp
lodMaxClamp:lodMaxClamp
atIndex:index];
[state ANGLE_MTL_RELEASE];
}
void SetVertexTextureCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
id<MTLTexture> texture = stream->fetch<id<MTLTexture>>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setVertexTexture:texture atIndex:index];
[texture ANGLE_MTL_RELEASE];
}
void SetFragmentBufferCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
id<MTLBuffer> buffer = stream->fetch<id<MTLBuffer>>();
uint32_t offset = stream->fetch<uint32_t>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setFragmentBuffer:buffer offset:offset atIndex:index];
[buffer ANGLE_MTL_RELEASE];
}
void SetFragmentBufferOffsetCmd(id<MTLRenderCommandEncoder> encoder,
IntermediateCommandStream *stream)
{
uint32_t offset = stream->fetch<uint32_t>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setFragmentBufferOffset:offset atIndex:index];
}
void SetFragmentBytesCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
size_t size = stream->fetch<size_t>();
const uint8_t *bytes = stream->fetch(size);
uint32_t index = stream->fetch<uint32_t>();
[encoder setFragmentBytes:bytes length:size atIndex:index];
}
void SetFragmentSamplerStateCmd(id<MTLRenderCommandEncoder> encoder,
IntermediateCommandStream *stream)
{
id<MTLSamplerState> state = stream->fetch<id<MTLSamplerState>>();
float lodMinClamp = stream->fetch<float>();
float lodMaxClamp = stream->fetch<float>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setFragmentSamplerState:state
lodMinClamp:lodMinClamp
lodMaxClamp:lodMaxClamp
atIndex:index];
[state ANGLE_MTL_RELEASE];
}
void SetFragmentTextureCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
id<MTLTexture> texture = stream->fetch<id<MTLTexture>>();
uint32_t index = stream->fetch<uint32_t>();
[encoder setFragmentTexture:texture atIndex:index];
[texture ANGLE_MTL_RELEASE];
}
void DrawCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>();
uint32_t vertexStart = stream->fetch<uint32_t>();
uint32_t vertexCount = stream->fetch<uint32_t>();
[encoder drawPrimitives:primitiveType vertexStart:vertexStart vertexCount:vertexCount];
}
void DrawInstancedCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>();
uint32_t vertexStart = stream->fetch<uint32_t>();
uint32_t vertexCount = stream->fetch<uint32_t>();
uint32_t instances = stream->fetch<uint32_t>();
[encoder drawPrimitives:primitiveType
vertexStart:vertexStart
vertexCount:vertexCount
instanceCount:instances];
}
void DrawIndexedCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>();
uint32_t indexCount = stream->fetch<uint32_t>();
MTLIndexType indexType = stream->fetch<MTLIndexType>();
id<MTLBuffer> indexBuffer = stream->fetch<id<MTLBuffer>>();
size_t bufferOffset = stream->fetch<size_t>();
[encoder drawIndexedPrimitives:primitiveType
indexCount:indexCount
indexType:indexType
indexBuffer:indexBuffer
indexBufferOffset:bufferOffset];
[indexBuffer ANGLE_MTL_RELEASE];
}
void DrawIndexedInstancedCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>();
uint32_t indexCount = stream->fetch<uint32_t>();
MTLIndexType indexType = stream->fetch<MTLIndexType>();
id<MTLBuffer> indexBuffer = stream->fetch<id<MTLBuffer>>();
size_t bufferOffset = stream->fetch<size_t>();
uint32_t instances = stream->fetch<uint32_t>();
[encoder drawIndexedPrimitives:primitiveType
indexCount:indexCount
indexType:indexType
indexBuffer:indexBuffer
indexBufferOffset:bufferOffset
instanceCount:instances];
[indexBuffer ANGLE_MTL_RELEASE];
}
void DrawIndexedInstancedBaseVertexCmd(id<MTLRenderCommandEncoder> encoder,
IntermediateCommandStream *stream)
{
MTLPrimitiveType primitiveType = stream->fetch<MTLPrimitiveType>();
uint32_t indexCount = stream->fetch<uint32_t>();
MTLIndexType indexType = stream->fetch<MTLIndexType>();
id<MTLBuffer> indexBuffer = stream->fetch<id<MTLBuffer>>();
size_t bufferOffset = stream->fetch<size_t>();
uint32_t instances = stream->fetch<uint32_t>();
uint32_t baseVertex = stream->fetch<uint32_t>();
[encoder drawIndexedPrimitives:primitiveType
indexCount:indexCount
indexType:indexType
indexBuffer:indexBuffer
indexBufferOffset:bufferOffset
instanceCount:instances
baseVertex:baseVertex
baseInstance:0];
[indexBuffer ANGLE_MTL_RELEASE];
}
// Command encoder mapping
#define ANGLE_MTL_CMD_MAP(CMD) CMD##Cmd,
using CommandEncoderFunc = void (*)(id<MTLRenderCommandEncoder>, IntermediateCommandStream *);
constexpr CommandEncoderFunc gCommandEncoders[] = {ANGLE_MTL_CMD_X(ANGLE_MTL_CMD_MAP)};
}
// CommandQueue implementation
void CommandQueue::reset()
{
......@@ -97,7 +400,18 @@ bool CommandQueue::isResourceBeingUsedByGPU(const Resource *resource) const
}
return mCompletedBufferSerial.load(std::memory_order_relaxed) <
resource->getCommandBufferQueueSerial().load(std::memory_order_relaxed);
resource->getCommandBufferQueueSerial();
}
bool CommandQueue::resourceHasPendingWorks(const Resource *resource) const
{
if (!resource)
{
return false;
}
return mCommittedBufferSerial.load(std::memory_order_relaxed) <
resource->getCommandBufferQueueSerial();
}
AutoObjCPtr<id<MTLCommandBuffer>> CommandQueue::makeMetalCommandBuffer(uint64_t *queueSerialOut)
......@@ -128,6 +442,17 @@ AutoObjCPtr<id<MTLCommandBuffer>> CommandQueue::makeMetalCommandBuffer(uint64_t
}
}
void CommandQueue::onCommandBufferCommitted(id<MTLCommandBuffer> buf, uint64_t serial)
{
std::lock_guard<std::mutex> lg(mLock);
ANGLE_MTL_LOG("Committed MTLCommandBuffer %llu:%p", serial, buf);
mCommittedBufferSerial.store(
std::max(mCommittedBufferSerial.load(std::memory_order_relaxed), serial),
std::memory_order_relaxed);
}
void CommandQueue::onCommandBufferCompleted(id<MTLCommandBuffer> buf, uint64_t serial)
{
std::lock_guard<std::mutex> lg(mLock);
......@@ -164,11 +489,11 @@ CommandBuffer::~CommandBuffer()
cleanup();
}
bool CommandBuffer::valid() const
bool CommandBuffer::ready() const
{
std::lock_guard<std::mutex> lg(mLock);
return validImpl();
return readyImpl();
}
void CommandBuffer::commit()
......@@ -197,7 +522,7 @@ void CommandBuffer::setWriteDependency(const ResourceRef &resource)
std::lock_guard<std::mutex> lg(mLock);
if (!validImpl())
if (!readyImpl())
{
return;
}
......@@ -214,7 +539,7 @@ void CommandBuffer::setReadDependency(const ResourceRef &resource)
std::lock_guard<std::mutex> lg(mLock);
if (!validImpl())
if (!readyImpl())
{
return;
}
......@@ -249,7 +574,10 @@ void CommandBuffer::setActiveCommandEncoder(CommandEncoder *encoder)
void CommandBuffer::invalidateActiveCommandEncoder(CommandEncoder *encoder)
{
mActiveCommandEncoder.compare_exchange_strong(encoder, nullptr);
if (mActiveCommandEncoder == encoder)
{
mActiveCommandEncoder = nullptr;
}
}
void CommandBuffer::cleanup()
......@@ -259,7 +587,7 @@ void CommandBuffer::cleanup()
ParentClass::set(nil);
}
bool CommandBuffer::validImpl() const
bool CommandBuffer::readyImpl() const
{
if (!ParentClass::valid())
{
......@@ -271,26 +599,32 @@ bool CommandBuffer::validImpl() const
void CommandBuffer::commitImpl()
{
if (!validImpl())
if (!readyImpl())
{
return;
}
// End the current encoder
if (mActiveCommandEncoder.load(std::memory_order_relaxed))
{
mActiveCommandEncoder.load(std::memory_order_relaxed)->endEncoding();
mActiveCommandEncoder = nullptr;
}
forceEndingCurrentEncoder();
// Notify command queue
mCmdQueue.onCommandBufferCommitted(get(), mQueueSerial);
// Do the actual commit
[get() commit];
ANGLE_MTL_LOG("Committed MTLCommandBuffer %llu:%p", mQueueSerial, get());
mCommitted = true;
}
void CommandBuffer::forceEndingCurrentEncoder()
{
if (mActiveCommandEncoder)
{
mActiveCommandEncoder->endEncoding();
mActiveCommandEncoder = nullptr;
}
}
// CommandEncoder implementation
CommandEncoder::CommandEncoder(CommandBuffer *cmdBuffer, Type type)
: mType(type), mCmdBuffer(*cmdBuffer)
......@@ -334,74 +668,207 @@ CommandEncoder &CommandEncoder::markResourceBeingWrittenByGPU(const TextureRef &
return *this;
}
// RenderCommandEncoder implemtation
RenderCommandEncoder::RenderCommandEncoder(CommandBuffer *cmdBuffer)
: CommandEncoder(cmdBuffer, RENDER)
{}
RenderCommandEncoder::~RenderCommandEncoder() {}
// RenderCommandEncoderShaderStates implementation
RenderCommandEncoderShaderStates::RenderCommandEncoderShaderStates()
{
reset();
}
void RenderCommandEncoder::endEncoding()
void RenderCommandEncoderShaderStates::reset()
{
if (!valid())
return;
for (id<MTLBuffer> &buffer : buffers)
{
buffer = nil;
}
// Now is the time to do the actual store option setting.
auto metalEncoder = get();
for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
for (uint32_t &offset : bufferOffsets)
{
if (mRenderPassDesc.colorAttachments[i].storeAction == MTLStoreActionUnknown)
{
// If storeAction hasn't been set for this attachment, we set to dontcare.
mRenderPassDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare;
}
offset = 0;
}
// Only initial unknown store action can change the value now.
if (mColorInitialStoreActions[i] == MTLStoreActionUnknown)
{
[metalEncoder setColorStoreAction:mRenderPassDesc.colorAttachments[i].storeAction
atIndex:i];
}
for (id<MTLSamplerState> &sampler : samplers)
{
sampler = nil;
}
if (mRenderPassDesc.depthAttachment.storeAction == MTLStoreActionUnknown)
for (Optional<std::pair<float, float>> &lodClampRange : samplerLodClamps)
{
// If storeAction hasn't been set for this attachment, we set to dontcare.
mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionDontCare;
lodClampRange.reset();
}
if (mDepthInitialStoreAction == MTLStoreActionUnknown)
for (id<MTLTexture> &texture : textures)
{
[metalEncoder setDepthStoreAction:mRenderPassDesc.depthAttachment.storeAction];
texture = nil;
}
}
if (mRenderPassDesc.stencilAttachment.storeAction == MTLStoreActionUnknown)
// RenderCommandEncoderStates implementation
RenderCommandEncoderStates::RenderCommandEncoderStates()
{
reset();
}
void RenderCommandEncoderStates::reset()
{
renderPipeline = nil;
triangleFillMode = MTLTriangleFillModeFill;
winding = MTLWindingClockwise;
cullMode = MTLCullModeNone;
depthStencilState = nil;
depthBias = depthSlopeScale = depthClamp = 0;
stencilFrontRef = stencilBackRef = 0;
viewport.reset();
scissorRect.reset();
blendColor = {0, 0, 0, 0};
for (RenderCommandEncoderShaderStates &shaderStates : perShaderStates)
{
shaderStates.reset();
}
}
// RenderCommandEncoder implemtation
RenderCommandEncoder::RenderCommandEncoder(CommandBuffer *cmdBuffer)
: CommandEncoder(cmdBuffer, RENDER)
{
ANGLE_MTL_OBJC_SCOPE
{
mCachedRenderPassDescObjC = [MTLRenderPassDescriptor renderPassDescriptor];
}
static_assert(sizeof(uint8_t) == sizeof(CmdType), "CmdType was expected to be 8 bit");
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mSetBufferCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid);
mSetBytesCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid);
mSetTextureCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid);
mSetSamplerCmds[shaderType] = static_cast<uint8_t>(CmdType::Invalid);
}
mSetBufferCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexBuffer);
mSetBufferCmds[gl::ShaderType::Fragment] = static_cast<uint8_t>(CmdType::SetFragmentBuffer);
mSetBufferOffsetCmds[gl::ShaderType::Vertex] =
static_cast<uint8_t>(CmdType::SetVertexBufferOffset);
mSetBufferOffsetCmds[gl::ShaderType::Fragment] =
static_cast<uint8_t>(CmdType::SetFragmentBufferOffset);
mSetBytesCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexBytes);
mSetBytesCmds[gl::ShaderType::Fragment] = static_cast<uint8_t>(CmdType::SetFragmentBytes);
mSetTextureCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexTexture);
mSetTextureCmds[gl::ShaderType::Fragment] = static_cast<uint8_t>(CmdType::SetFragmentTexture);
mSetSamplerCmds[gl::ShaderType::Vertex] = static_cast<uint8_t>(CmdType::SetVertexSamplerState);
mSetSamplerCmds[gl::ShaderType::Fragment] =
static_cast<uint8_t>(CmdType::SetFragmentSamplerState);
}
RenderCommandEncoder::~RenderCommandEncoder() {}
void RenderCommandEncoder::reset()
{
CommandEncoder::reset();
mRecording = false;
mCommands.clear();
}
void RenderCommandEncoder::finalizeLoadStoreAction(
MTLRenderPassAttachmentDescriptor *objCRenderPassAttachment)
{
if (!objCRenderPassAttachment.texture)
{
objCRenderPassAttachment.loadAction = MTLLoadActionDontCare;
objCRenderPassAttachment.storeAction = MTLStoreActionDontCare;
objCRenderPassAttachment.resolveTexture = nil;
return;
}
if (objCRenderPassAttachment.storeAction == MTLStoreActionUnknown)
{
// If storeAction hasn't been set for this attachment, we set to dontcare.
mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare;
objCRenderPassAttachment.storeAction = MTLStoreActionDontCare;
}
if (mStencilInitialStoreAction == MTLStoreActionUnknown)
}
void RenderCommandEncoder::endEncoding()
{
if (!valid())
return;
// Last minute correcting the store options.
MTLRenderPassDescriptor *objCRenderPassDesc = mCachedRenderPassDescObjC.get();
for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
{
[metalEncoder setStencilStoreAction:mRenderPassDesc.stencilAttachment.storeAction];
// Update store action set between restart() and endEncoding()
objCRenderPassDesc.colorAttachments[i].storeAction =
mRenderPassDesc.colorAttachments[i].storeAction;
finalizeLoadStoreAction(objCRenderPassDesc.colorAttachments[i]);
}
// Update depth store action set between restart() and endEncoding()
objCRenderPassDesc.depthAttachment.storeAction = mRenderPassDesc.depthAttachment.storeAction;
finalizeLoadStoreAction(objCRenderPassDesc.depthAttachment);
// Update stencil store action set between restart() and endEncoding()
objCRenderPassDesc.stencilAttachment.storeAction =
mRenderPassDesc.stencilAttachment.storeAction;
finalizeLoadStoreAction(objCRenderPassDesc.stencilAttachment);
// Encode the actual encoder
encodeMetalEncoder();
CommandEncoder::endEncoding();
// reset state
mRenderPassDesc = RenderPassDesc();
mStateCache.reset();
}
inline void RenderCommandEncoder::initWriteDependencyAndStoreAction(const TextureRef &texture,
MTLStoreAction *storeActionOut)
inline void RenderCommandEncoder::initAttachmentWriteDependencyAndScissorRect(
const RenderPassAttachmentDesc &attachment)
{
TextureRef texture = attachment.texture;
if (texture)
{
cmdBuffer().setWriteDependency(texture);
// Set initial store action to unknown so that we can change it later when the encoder ends.
*storeActionOut = MTLStoreActionUnknown;
uint32_t mipLevel = attachment.level;
mRenderPassMaxScissorRect.width =
std::min<NSUInteger>(mRenderPassMaxScissorRect.width, texture->width(mipLevel));
mRenderPassMaxScissorRect.height =
std::min<NSUInteger>(mRenderPassMaxScissorRect.height, texture->height(mipLevel));
}
else
}
void RenderCommandEncoder::encodeMetalEncoder()
{
ANGLE_MTL_OBJC_SCOPE
{
// Texture is invalid, use don'tcare store action
*storeActionOut = MTLStoreActionDontCare;
ANGLE_MTL_LOG("Creating new render command encoder with desc: %@",
mCachedRenderPassDescObjC.get());
id<MTLRenderCommandEncoder> metalCmdEncoder =
[cmdBuffer().get() renderCommandEncoderWithDescriptor:mCachedRenderPassDescObjC];
set(metalCmdEncoder);
// Verify that it was created successfully
ASSERT(metalCmdEncoder);
while (mCommands.good())
{
CmdType cmdType = mCommands.fetch<CmdType>();
CommandEncoderFunc encoder = gCommandEncoders[static_cast<int>(cmdType)];
encoder(metalCmdEncoder, &mCommands);
}
mCommands.clear();
}
}
......@@ -419,86 +886,102 @@ RenderCommandEncoder &RenderCommandEncoder::restart(const RenderPassDesc &desc)
endEncoding();
}
if (!cmdBuffer().valid())
if (!cmdBuffer().ready())
{
reset();
return *this;
}
mRenderPassDesc = desc;
mRenderPassDesc = desc;
mRecording = true;
mHasDrawCalls = false;
mRenderPassMaxScissorRect = {.x = 0,
.y = 0,
.width = std::numeric_limits<NSUInteger>::max(),
.height = std::numeric_limits<NSUInteger>::max()};
ANGLE_MTL_OBJC_SCOPE
// Set writing dependency & constrain the scissor rect
for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
{
// mask writing dependency
for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
{
initWriteDependencyAndStoreAction(mRenderPassDesc.colorAttachments[i].texture,
&mRenderPassDesc.colorAttachments[i].storeAction);
mColorInitialStoreActions[i] = mRenderPassDesc.colorAttachments[i].storeAction;
}
initWriteDependencyAndStoreAction(mRenderPassDesc.depthAttachment.texture,
&mRenderPassDesc.depthAttachment.storeAction);
mDepthInitialStoreAction = mRenderPassDesc.depthAttachment.storeAction;
initWriteDependencyAndStoreAction(mRenderPassDesc.stencilAttachment.texture,
&mRenderPassDesc.stencilAttachment.storeAction);
mStencilInitialStoreAction = mRenderPassDesc.stencilAttachment.storeAction;
// Create objective C object
mtl::AutoObjCObj<MTLRenderPassDescriptor> objCDesc = ToMetalObj(mRenderPassDesc);
ANGLE_MTL_LOG("Creating new render command encoder with desc: %@", objCDesc.get());
initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.colorAttachments[i]);
}
id<MTLRenderCommandEncoder> metalCmdEncoder =
[cmdBuffer().get() renderCommandEncoderWithDescriptor:objCDesc];
initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.depthAttachment);
set(metalCmdEncoder);
initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.stencilAttachment);
// Set the actual store action
for (uint32_t i = 0; i < desc.numColorAttachments; ++i)
{
setColorStoreAction(desc.colorAttachments[i].storeAction, i);
}
// Convert to Objective-C descriptor
mRenderPassDesc.convertToMetalDesc(mCachedRenderPassDescObjC);
setDepthStencilStoreAction(desc.depthAttachment.storeAction,
desc.stencilAttachment.storeAction);
// The actual Objective-C encoder will be created later in endEncoding(), we do so in
// order to be able to sort the commands or do the preprocessing before the actual
// encoding.
// Verify that it was created successfully
ASSERT(get());
} // ANGLE_MTL_OBJC_SCOPE
// Since we defer the native encoder creation, we need to explicitly tell command buffer
// that this object is the active encoder:
cmdBuffer().setActiveCommandEncoder(this);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setRenderPipelineState(id<MTLRenderPipelineState> state)
{
[get() setRenderPipelineState:state];
if (mStateCache.renderPipeline == state)
{
return *this;
}
mStateCache.renderPipeline = state;
mCommands.push(CmdType::SetRenderPipelineState).push([state ANGLE_MTL_RETAIN]);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setTriangleFillMode(MTLTriangleFillMode mode)
{
[get() setTriangleFillMode:mode];
if (mStateCache.triangleFillMode == mode)
{
return *this;
}
mStateCache.triangleFillMode = mode;
mCommands.push(CmdType::SetTriangleFillMode).push(mode);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setFrontFacingWinding(MTLWinding winding)
{
[get() setFrontFacingWinding:winding];
if (mStateCache.winding == winding)
{
return *this;
}
mStateCache.winding = winding;
mCommands.push(CmdType::SetFrontFacingWinding).push(winding);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setCullMode(MTLCullMode mode)
{
[get() setCullMode:mode];
if (mStateCache.cullMode == mode)
{
return *this;
}
mStateCache.cullMode = mode;
mCommands.push(CmdType::SetCullMode).push(mode);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setDepthStencilState(id<MTLDepthStencilState> state)
{
[get() setDepthStencilState:state];
if (mStateCache.depthStencilState == state)
{
return *this;
}
mStateCache.depthStencilState = state;
mCommands.push(CmdType::SetDepthStencilState).push([state ANGLE_MTL_RETAIN]);
return *this;
}
......@@ -506,7 +989,16 @@ RenderCommandEncoder &RenderCommandEncoder::setDepthBias(float depthBias,
float slopeScale,
float clamp)
{
[get() setDepthBias:depthBias slopeScale:slopeScale clamp:clamp];
if (mStateCache.depthBias == depthBias && mStateCache.depthSlopeScale == slopeScale &&
mStateCache.depthClamp == clamp)
{
return *this;
}
mStateCache.depthBias = depthBias;
mStateCache.depthSlopeScale = slopeScale;
mStateCache.depthClamp = clamp;
mCommands.push(CmdType::SetDepthBias).push(depthBias).push(slopeScale).push(clamp);
return *this;
}
......@@ -515,7 +1007,15 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilRefVals(uint32_t frontRef,
// Metal has some bugs when reference values are larger than 0xff
ASSERT(frontRef == (frontRef & kStencilMaskAll));
ASSERT(backRef == (backRef & kStencilMaskAll));
[get() setStencilFrontReferenceValue:frontRef backReferenceValue:backRef];
if (mStateCache.stencilFrontRef == frontRef && mStateCache.stencilBackRef == backRef)
{
return *this;
}
mStateCache.stencilFrontRef = frontRef;
mStateCache.stencilBackRef = backRef;
mCommands.push(CmdType::SetStencilRefVals).push(frontRef).push(backRef);
return *this;
}
......@@ -527,28 +1027,61 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilRefVal(uint32_t ref)
RenderCommandEncoder &RenderCommandEncoder::setViewport(const MTLViewport &viewport)
{
[get() setViewport:viewport];
if (mStateCache.viewport.valid() && mStateCache.viewport.value() == viewport)
{
return *this;
}
mStateCache.viewport = viewport;
mCommands.push(CmdType::SetViewport).push(viewport);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setScissorRect(const MTLScissorRect &rect)
{
[get() setScissorRect:rect];
if (mStateCache.scissorRect.valid() && mStateCache.scissorRect.value() == rect)
{
return *this;
}
if (ANGLE_UNLIKELY(rect.x + rect.width > mRenderPassMaxScissorRect.width ||
rect.y + rect.height > mRenderPassMaxScissorRect.height))
{
WARN() << "Out of bound scissor rect detected " << rect.x << " " << rect.y << " "
<< rect.width << " " << rect.height;
// Out of bound rect will crash the metal runtime, ignore it.
return *this;
}
mStateCache.scissorRect = rect;
mCommands.push(CmdType::SetScissorRect).push(rect);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setBlendColor(float r, float g, float b, float a)
{
[get() setBlendColorRed:r green:g blue:b alpha:a];
if (mStateCache.blendColor[0] == r && mStateCache.blendColor[1] == g &&
mStateCache.blendColor[2] == b && mStateCache.blendColor[3] == a)
{
return *this;
}
mStateCache.blendColor[0] = r;
mStateCache.blendColor[1] = g;
mStateCache.blendColor[2] = b;
mStateCache.blendColor[3] = a;
mCommands.push(CmdType::SetBlendColor).push(r).push(g).push(b).push(a);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setVertexBuffer(const BufferRef &buffer,
uint32_t offset,
uint32_t index)
RenderCommandEncoder &RenderCommandEncoder::setBuffer(gl::ShaderType shaderType,
const BufferRef &buffer,
uint32_t offset,
uint32_t index)
{
if (index >= kMaxShaderBuffers)
{
......@@ -557,113 +1090,140 @@ RenderCommandEncoder &RenderCommandEncoder::setVertexBuffer(const BufferRef &buf
cmdBuffer().setReadDependency(buffer);
[get() setVertexBuffer:(buffer ? buffer->get() : nil) offset:offset atIndex:index];
id<MTLBuffer> mtlBuffer = (buffer ? buffer->get() : nil);
return *this;
return commonSetBuffer(shaderType, mtlBuffer, offset, index);
}
RenderCommandEncoder &RenderCommandEncoder::setVertexBytes(const uint8_t *bytes,
size_t size,
uint32_t index)
RenderCommandEncoder &RenderCommandEncoder::setBufferForWrite(gl::ShaderType shaderType,
const BufferRef &buffer,
uint32_t offset,
uint32_t index)
{
if (index >= kMaxShaderBuffers)
{
return *this;
}
[get() setVertexBytes:bytes length:size atIndex:index];
cmdBuffer().setWriteDependency(buffer);
id<MTLBuffer> mtlBuffer = (buffer ? buffer->get() : nil);
return *this;
return commonSetBuffer(shaderType, mtlBuffer, offset, index);
}
RenderCommandEncoder &RenderCommandEncoder::setVertexSamplerState(id<MTLSamplerState> state,
float lodMinClamp,
float lodMaxClamp,
uint32_t index)
RenderCommandEncoder &RenderCommandEncoder::commonSetBuffer(gl::ShaderType shaderType,
id<MTLBuffer> mtlBuffer,
uint32_t offset,
uint32_t index)
{
if (index >= kMaxShaderSamplers)
RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType];
if (shaderStates.buffers[index] == mtlBuffer)
{
return *this;
}
if (shaderStates.bufferOffsets[index] == offset)
{
return *this;
}
[get() setVertexSamplerState:state
lodMinClamp:lodMinClamp
lodMaxClamp:lodMaxClamp
atIndex:index];
// If buffer already bound but with different offset, then update the offset only.
shaderStates.bufferOffsets[index] = offset;
mCommands.push(static_cast<CmdType>(mSetBufferOffsetCmds[shaderType]))
.push(offset)
.push(index);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setVertexTexture(const TextureRef &texture,
uint32_t index)
{
if (index >= kMaxShaderSamplers)
{
return *this;
}
cmdBuffer().setReadDependency(texture);
[get() setVertexTexture:(texture ? texture->get() : nil) atIndex:index];
shaderStates.buffers[index] = mtlBuffer;
shaderStates.bufferOffsets[index] = offset;
mCommands.push(static_cast<CmdType>(mSetBufferCmds[shaderType]))
.push([mtlBuffer ANGLE_MTL_RETAIN])
.push(offset)
.push(index);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setFragmentBuffer(const BufferRef &buffer,
uint32_t offset,
uint32_t index)
RenderCommandEncoder &RenderCommandEncoder::setBytes(gl::ShaderType shaderType,
const uint8_t *bytes,
size_t size,
uint32_t index)
{
if (index >= kMaxShaderBuffers)
{
return *this;
}
cmdBuffer().setReadDependency(buffer);
RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType];
shaderStates.buffers[index] = nil;
shaderStates.bufferOffsets[index] = 0;
[get() setFragmentBuffer:(buffer ? buffer->get() : nil) offset:offset atIndex:index];
mCommands.push(static_cast<CmdType>(mSetBytesCmds[shaderType]))
.push(size)
.push(bytes, size)
.push(index);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setFragmentBytes(const uint8_t *bytes,
size_t size,
uint32_t index)
RenderCommandEncoder &RenderCommandEncoder::setSamplerState(gl::ShaderType shaderType,
id<MTLSamplerState> state,
float lodMinClamp,
float lodMaxClamp,
uint32_t index)
{
if (index >= kMaxShaderBuffers)
if (index >= kMaxShaderSamplers)
{
return *this;
}
[get() setFragmentBytes:bytes length:size atIndex:index];
RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType];
if (shaderStates.samplers[index] == state && shaderStates.samplerLodClamps[index].valid())
{
const std::pair<float, float> &currentLodClampRange =
shaderStates.samplerLodClamps[index].value();
if (currentLodClampRange.first == lodMinClamp && currentLodClampRange.second == lodMaxClamp)
{
return *this;
}
}
shaderStates.samplers[index] = state;
shaderStates.samplerLodClamps[index] = {lodMinClamp, lodMaxClamp};
mCommands.push(static_cast<CmdType>(mSetSamplerCmds[shaderType]))
.push([state ANGLE_MTL_RETAIN])
.push(lodMinClamp)
.push(lodMaxClamp)
.push(index);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setFragmentSamplerState(id<MTLSamplerState> state,
float lodMinClamp,
float lodMaxClamp,
uint32_t index)
RenderCommandEncoder &RenderCommandEncoder::setTexture(gl::ShaderType shaderType,
const TextureRef &texture,
uint32_t index)
{
if (index >= kMaxShaderSamplers)
{
return *this;
}
[get() setFragmentSamplerState:state
lodMinClamp:lodMinClamp
lodMaxClamp:lodMaxClamp
atIndex:index];
cmdBuffer().setReadDependency(texture);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setFragmentTexture(const TextureRef &texture,
uint32_t index)
{
if (index >= kMaxShaderSamplers)
id<MTLTexture> mtlTexture = (texture ? texture->get() : nil);
RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType];
if (shaderStates.textures[index] == mtlTexture)
{
return *this;
}
shaderStates.textures[index] = mtlTexture;
cmdBuffer().setReadDependency(texture);
[get() setFragmentTexture:(texture ? texture->get() : nil) atIndex:index];
mCommands.push(static_cast<CmdType>(mSetTextureCmds[shaderType]))
.push([mtlTexture ANGLE_MTL_RETAIN])
.push(index);
return *this;
}
......@@ -672,7 +1232,14 @@ RenderCommandEncoder &RenderCommandEncoder::draw(MTLPrimitiveType primitiveType,
uint32_t vertexStart,
uint32_t vertexCount)
{
[get() drawPrimitives:primitiveType vertexStart:vertexStart vertexCount:vertexCount];
if (ANGLE_UNLIKELY(!mStateCache.renderPipeline))
{
// Ignore draw call if there is no render pipeline state set prior to this.
return *this;
}
mHasDrawCalls = true;
mCommands.push(CmdType::Draw).push(primitiveType).push(vertexStart).push(vertexCount);
return *this;
}
......@@ -682,10 +1249,18 @@ RenderCommandEncoder &RenderCommandEncoder::drawInstanced(MTLPrimitiveType primi
uint32_t vertexCount,
uint32_t instances)
{
[get() drawPrimitives:primitiveType
vertexStart:vertexStart
vertexCount:vertexCount
instanceCount:instances];
if (ANGLE_UNLIKELY(!mStateCache.renderPipeline))
{
// Ignore draw call if there is no render pipeline state set prior to this.
return *this;
}
mHasDrawCalls = true;
mCommands.push(CmdType::DrawInstanced)
.push(primitiveType)
.push(vertexStart)
.push(vertexCount)
.push(instances);
return *this;
}
......@@ -696,17 +1271,26 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primiti
const BufferRef &indexBuffer,
size_t bufferOffset)
{
if (!indexBuffer)
if (ANGLE_UNLIKELY(!mStateCache.renderPipeline))
{
// Ignore draw call if there is no render pipeline state set prior to this.
return *this;
}
if (ANGLE_UNLIKELY(!indexBuffer))
{
return *this;
}
mHasDrawCalls = true;
cmdBuffer().setReadDependency(indexBuffer);
[get() drawIndexedPrimitives:primitiveType
indexCount:indexCount
indexType:indexType
indexBuffer:indexBuffer->get()
indexBufferOffset:bufferOffset];
mCommands.push(CmdType::DrawIndexed)
.push(primitiveType)
.push(indexCount)
.push(indexType)
.push([indexBuffer->get() ANGLE_MTL_RETAIN])
.push(bufferOffset);
return *this;
}
......@@ -718,18 +1302,27 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstanced(MTLPrimitiveTyp
size_t bufferOffset,
uint32_t instances)
{
if (!indexBuffer)
if (ANGLE_UNLIKELY(!mStateCache.renderPipeline))
{
// Ignore draw call if there is no render pipeline state set prior to this.
return *this;
}
if (ANGLE_UNLIKELY(!indexBuffer))
{
return *this;
}
mHasDrawCalls = true;
cmdBuffer().setReadDependency(indexBuffer);
[get() drawIndexedPrimitives:primitiveType
indexCount:indexCount
indexType:indexType
indexBuffer:indexBuffer->get()
indexBufferOffset:bufferOffset
instanceCount:instances];
mCommands.push(CmdType::DrawIndexedInstanced)
.push(primitiveType)
.push(indexCount)
.push(indexType)
.push([indexBuffer->get() ANGLE_MTL_RETAIN])
.push(bufferOffset)
.push(instances);
return *this;
}
......@@ -743,20 +1336,28 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstancedBaseVertex(
uint32_t instances,
uint32_t baseVertex)
{
if (!indexBuffer)
if (ANGLE_UNLIKELY(!mStateCache.renderPipeline))
{
// Ignore draw call if there is no render pipeline state set prior to this.
return *this;
}
if (ANGLE_UNLIKELY(!indexBuffer))
{
return *this;
}
mHasDrawCalls = true;
cmdBuffer().setReadDependency(indexBuffer);
[get() drawIndexedPrimitives:primitiveType
indexCount:indexCount
indexType:indexType
indexBuffer:indexBuffer->get()
indexBufferOffset:bufferOffset
instanceCount:instances
baseVertex:baseVertex
baseInstance:0];
mCommands.push(CmdType::DrawIndexedInstancedBaseVertex)
.push(primitiveType)
.push(indexCount)
.push(indexType)
.push([indexBuffer->get() ANGLE_MTL_RETAIN])
.push(bufferOffset)
.push(instances)
.push(baseVertex);
return *this;
}
......@@ -811,6 +1412,44 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilStoreAction(MTLStoreAction
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setColorLoadAction(MTLLoadAction action,
const MTLClearColor &clearValue,
uint32_t colorAttachmentIndex)
{
ASSERT(!hasDrawCalls());
if (mCachedRenderPassDescObjC.get().colorAttachments[colorAttachmentIndex].texture)
{
mCachedRenderPassDescObjC.get().colorAttachments[colorAttachmentIndex].loadAction = action;
mCachedRenderPassDescObjC.get().colorAttachments[colorAttachmentIndex].clearColor =
clearValue;
}
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setDepthLoadAction(MTLLoadAction action,
double clearVal)
{
ASSERT(!hasDrawCalls());
if (mCachedRenderPassDescObjC.get().depthAttachment.texture)
{
mCachedRenderPassDescObjC.get().depthAttachment.loadAction = action;
mCachedRenderPassDescObjC.get().depthAttachment.clearDepth = clearVal;
}
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setStencilLoadAction(MTLLoadAction action,
uint32_t clearVal)
{
ASSERT(!hasDrawCalls());
if (mCachedRenderPassDescObjC.get().stencilAttachment.texture)
{
mCachedRenderPassDescObjC.get().stencilAttachment.loadAction = action;
mCachedRenderPassDescObjC.get().stencilAttachment.clearStencil = clearVal;
}
return *this;
}
// BlitCommandEncoder
BlitCommandEncoder::BlitCommandEncoder(CommandBuffer *cmdBuffer) : CommandEncoder(cmdBuffer, BLIT)
{}
......@@ -827,7 +1466,7 @@ BlitCommandEncoder &BlitCommandEncoder::restart()
return *this;
}
if (!cmdBuffer().valid())
if (!cmdBuffer().ready())
{
reset();
return *this;
......@@ -876,15 +1515,15 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src
return *this;
}
BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin,
MTLSize dstSize,
const TextureRef &src,
BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src,
uint32_t srcSlice,
uint32_t srcLevel,
MTLOrigin srcOrigin)
MTLOrigin srcOrigin,
MTLSize srcSize,
const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin)
{
if (!src || !dst)
{
......@@ -897,7 +1536,7 @@ BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &dst,
sourceSlice:srcSlice
sourceLevel:srcLevel
sourceOrigin:srcOrigin
sourceSize:dstSize
sourceSize:srcSize
toTexture:dst->get()
destinationSlice:dstSlice
destinationLevel:dstLevel
......@@ -928,8 +1567,15 @@ BlitCommandEncoder &BlitCommandEncoder::synchronizeResource(const TextureRef &te
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
// Only MacOS has separated storage for resource on CPU and GPU and needs explicit
// synchronization
cmdBuffer().setWriteDependency(texture);
[get() synchronizeResource:texture->get()];
cmdBuffer().setReadDependency(texture);
if (texture->get().parentTexture)
{
[get() synchronizeResource:texture->get().parentTexture];
}
else
{
[get() synchronizeResource:texture->get()];
}
#endif
return *this;
}
......@@ -950,7 +1596,7 @@ ComputeCommandEncoder &ComputeCommandEncoder::restart()
return *this;
}
if (!cmdBuffer().valid())
if (!cmdBuffer().ready())
{
reset();
return *this;
......@@ -982,15 +1628,26 @@ ComputeCommandEncoder &ComputeCommandEncoder::setBuffer(const BufferRef &buffer,
return *this;
}
// NOTE(hqle): Assume compute shader both reads and writes to this buffer for now.
cmdBuffer().setReadDependency(buffer);
cmdBuffer().setWriteDependency(buffer);
[get() setBuffer:(buffer ? buffer->get() : nil) offset:offset atIndex:index];
return *this;
}
ComputeCommandEncoder &ComputeCommandEncoder::setBufferForWrite(const BufferRef &buffer,
uint32_t offset,
uint32_t index)
{
if (index >= kMaxShaderBuffers)
{
return *this;
}
cmdBuffer().setWriteDependency(buffer);
return setBuffer(buffer, offset, index);
}
ComputeCommandEncoder &ComputeCommandEncoder::setBytes(const uint8_t *bytes,
size_t size,
uint32_t index)
......@@ -1026,27 +1683,39 @@ ComputeCommandEncoder &ComputeCommandEncoder::setTexture(const TextureRef &textu
return *this;
}
// NOTE(hqle): Assume compute shader both reads and writes to this texture for now.
cmdBuffer().setReadDependency(texture);
cmdBuffer().setWriteDependency(texture);
[get() setTexture:(texture ? texture->get() : nil) atIndex:index];
return *this;
}
ComputeCommandEncoder &ComputeCommandEncoder::setTextureForWrite(const TextureRef &texture,
uint32_t index)
{
if (index >= kMaxShaderSamplers)
{
return *this;
}
cmdBuffer().setWriteDependency(texture);
return setTexture(texture, index);
}
ComputeCommandEncoder &ComputeCommandEncoder::dispatch(MTLSize threadGroupsPerGrid,
MTLSize threadsPerGroup)
ComputeCommandEncoder &ComputeCommandEncoder::dispatch(const MTLSize &threadGroupsPerGrid,
const MTLSize &threadsPerGroup)
{
[get() dispatchThreadgroups:threadGroupsPerGrid threadsPerThreadgroup:threadsPerGroup];
return *this;
}
ComputeCommandEncoder &ComputeCommandEncoder::dispatchNonUniform(MTLSize threadsPerGrid,
MTLSize threadsPerGroup)
ComputeCommandEncoder &ComputeCommandEncoder::dispatchNonUniform(const MTLSize &threadsPerGrid,
const MTLSize &threadsPerGroup)
{
#if TARGET_OS_TV
UNREACHABLE();
#else
[get() dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerGroup];
#endif
return *this;
}
}
}
......@@ -35,8 +35,12 @@
#if !__has_feature(objc_arc)
# define ANGLE_MTL_AUTORELEASE autorelease
# define ANGLE_MTL_RETAIN retain
# define ANGLE_MTL_RELEASE release
#else
# define ANGLE_MTL_AUTORELEASE self
# define ANGLE_MTL_RETAIN self
# define ANGLE_MTL_RELEASE self
#endif
#define ANGLE_MTL_UNUSED __attribute__((unused))
......
......@@ -846,7 +846,7 @@ angle::Result IndexGeneratorUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
cmdEncoder->setData(uniform, 0);
cmdEncoder->setBuffer(params.srcBuffer, 0, 1);
cmdEncoder->setBuffer(params.dstBuffer, params.dstOffset, 2);
cmdEncoder->setBufferForWrite(params.dstBuffer, params.dstOffset, 2);
DispatchCompute(contextMtl, cmdEncoder, pipelineState, params.indexCount);
......@@ -878,7 +878,7 @@ angle::Result IndexGeneratorUtils::generateTriFanBufferFromArrays(
uniform.vertexCountFrom3rd = params.vertexCount - 2;
cmdEncoder->setData(uniform, 0);
cmdEncoder->setBuffer(params.dstBuffer, params.dstOffset, 2);
cmdEncoder->setBufferForWrite(params.dstBuffer, params.dstOffset, 2);
DispatchCompute(contextMtl, cmdEncoder, mTriFanFromArraysGeneratorPipeline,
uniform.vertexCountFrom3rd);
......@@ -937,7 +937,7 @@ angle::Result IndexGeneratorUtils::generateTriFanBufferFromElementsArrayGPU(
cmdEncoder->setData(uniform, 0);
cmdEncoder->setBuffer(srcBuffer, 0, 1);
cmdEncoder->setBuffer(dstBuffer, dstOffset, 2);
cmdEncoder->setBufferForWrite(dstBuffer, dstOffset, 2);
DispatchCompute(contextMtl, cmdEncoder, pipelineState, uniform.indexCount);
......
......@@ -49,35 +49,36 @@ class Resource : angle::NonCopyable
public:
virtual ~Resource() {}
// Check whether the resource still being used by GPU
bool isBeingUsedByGPU(Context *context) const;
// Checks whether the last command buffer that uses the given resource has been committed or not
bool hasPendingWorks(Context *context) const;
void setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing);
const std::atomic<uint64_t> &getCommandBufferQueueSerial() const
{
return mUsageRef->cmdBufferQueueSerial;
}
uint64_t getCommandBufferQueueSerial() const { return mUsageRef->cmdBufferQueueSerial; }
// Flag indicate whether we should synchornize the content to CPU after GPU changed this
// Flag indicate whether we should synchronize the content to CPU after GPU changed this
// resource's content.
bool isCPUReadMemDirty() const { return mUsageRef->cpuReadMemDirty; }
void resetCPUReadMemDirty() { mUsageRef->cpuReadMemDirty = false; }
bool isCPUReadMemNeedSync() const { return mUsageRef->cpuReadMemNeedSync; }
void resetCPUReadMemNeedSync() { mUsageRef->cpuReadMemNeedSync = false; }
protected:
Resource();
// Share the GPU usage ref with other resource
Resource(Resource *other);
void reset();
private:
struct UsageRef
{
// The id of the last command buffer that is using this resource.
std::atomic<uint64_t> cmdBufferQueueSerial{0};
uint64_t cmdBufferQueueSerial = 0;
// NOTE(hqle): resource dirty handle is not threadsafe.
// This flag means the resource was issued to be modified by GPU, if CPU wants to read
// its content, explicit synchornization call must be invoked.
bool cpuReadMemDirty = false;
// its content, explicit synchronization call must be invoked.
bool cpuReadMemNeedSync = false;
};
// One resource object might just be a view of another resource. For example, a texture 2d
......
......@@ -63,29 +63,30 @@ Resource::Resource(Resource *other) : mUsageRef(other->mUsageRef)
ASSERT(mUsageRef);
}
void Resource::reset()
{
mUsageRef->cmdBufferQueueSerial = 0;
resetCPUReadMemNeedSync();
}
bool Resource::isBeingUsedByGPU(Context *context) const
{
return context->cmdQueue().isResourceBeingUsedByGPU(this);
}
void Resource::setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing)
bool Resource::hasPendingWorks(Context *context) const
{
auto curSerial = mUsageRef->cmdBufferQueueSerial.load(std::memory_order_relaxed);
do
{
if (curSerial >= serial)
{
return;
}
} while (!mUsageRef->cmdBufferQueueSerial.compare_exchange_weak(
curSerial, serial, std::memory_order_release, std::memory_order_relaxed));
return context->cmdQueue().resourceHasPendingWorks(this);
}
// NOTE(hqle): This is not thread safe, if multiple command buffers on multiple threads
// are writing to it.
void Resource::setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing)
{
if (writing)
{
mUsageRef->cpuReadMemDirty = true;
mUsageRef->cpuReadMemNeedSync = true;
}
mUsageRef->cmdBufferQueueSerial = std::max(mUsageRef->cmdBufferQueueSerial, serial);
}
// Texture implemenetation
......@@ -234,6 +235,8 @@ void Texture::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEnco
if (blitEncoder)
{
blitEncoder->synchronizeResource(shared_from_this());
this->resetCPUReadMemNeedSync();
}
#endif
}
......@@ -244,12 +247,10 @@ void Texture::syncContent(ContextMtl *context)
// Make sure GPU & CPU contents are synchronized.
// NOTE: Only MacOS has separated storage for resource on CPU and GPU and needs explicit
// synchronization
if (this->isCPUReadMemDirty())
if (this->isCPUReadMemNeedSync())
{
mtl::BlitCommandEncoder *blitEncoder = context->getBlitCommandEncoder();
syncContent(context, blitEncoder);
this->resetCPUReadMemDirty();
}
#endif
}
......
......@@ -315,6 +315,8 @@ struct RenderPassDesc
RenderPassDepthAttachmentDesc depthAttachment;
RenderPassStencilAttachmentDesc stencilAttachment;
void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const;
// This will populate the RenderPipelineOutputDesc with default blend state and
// MTLColorWriteMaskAll
void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const;
......@@ -333,8 +335,6 @@ struct RenderPassDesc
uint32_t numColorAttachments = 0;
};
// convert to Metal object
AutoObjCObj<MTLRenderPassDescriptor> ToMetalObj(const RenderPassDesc &desc);
} // namespace mtl
} // namespace rx
......
......@@ -171,7 +171,8 @@ id<MTLTexture> ToObjC(const TextureRef &texture)
return textureRef ? textureRef->get() : nil;
}
void ToObjC(MTLRenderPassAttachmentDescriptor *dst, const RenderPassAttachmentDesc &src)
void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
MTLRenderPassAttachmentDescriptor *dst)
{
ANGLE_OBJC_CP_PROPERTY(dst, src, texture);
ANGLE_OBJC_CP_PROPERTY(dst, src, level);
......@@ -182,40 +183,28 @@ void ToObjC(MTLRenderPassAttachmentDescriptor *dst, const RenderPassAttachmentDe
ANGLE_OBJC_CP_PROPERTY(dst, src, storeActionOptions);
}
MTLRenderPassColorAttachmentDescriptor *ToObjC(const RenderPassColorAttachmentDesc &desc)
void ToObjC(const RenderPassColorAttachmentDesc &desc,
MTLRenderPassColorAttachmentDescriptor *objCDesc)
{
MTLRenderPassColorAttachmentDescriptor *objCDesc =
[[MTLRenderPassColorAttachmentDescriptor alloc] init];
ToObjC(objCDesc, desc);
BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearColor);
return [objCDesc ANGLE_MTL_AUTORELEASE];
}
MTLRenderPassDepthAttachmentDescriptor *ToObjC(const RenderPassDepthAttachmentDesc &desc)
void ToObjC(const RenderPassDepthAttachmentDesc &desc,
MTLRenderPassDepthAttachmentDescriptor *objCDesc)
{
MTLRenderPassDepthAttachmentDescriptor *objCDesc =
[[MTLRenderPassDepthAttachmentDescriptor alloc] init];
ToObjC(objCDesc, desc);
BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearDepth);
return [objCDesc ANGLE_MTL_AUTORELEASE];
}
MTLRenderPassStencilAttachmentDescriptor *ToObjC(const RenderPassStencilAttachmentDesc &desc)
void ToObjC(const RenderPassStencilAttachmentDesc &desc,
MTLRenderPassStencilAttachmentDescriptor *objCDesc)
{
MTLRenderPassStencilAttachmentDescriptor *objCDesc =
[[MTLRenderPassStencilAttachmentDescriptor alloc] init];
ToObjC(objCDesc, desc);
BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearStencil);
return [objCDesc ANGLE_MTL_AUTORELEASE];
}
} // namespace
......@@ -756,22 +745,27 @@ bool RenderPassDesc::operator==(const RenderPassDesc &other) const
}
// Convert to Metal object
AutoObjCObj<MTLRenderPassDescriptor> ToMetalObj(const RenderPassDesc &desc)
void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const
{
ANGLE_MTL_OBJC_SCOPE
{
MTLRenderPassDescriptor *objCDesc = [MTLRenderPassDescriptor renderPassDescriptor];
for (uint32_t i = 0; i < desc.numColorAttachments; ++i)
for (uint32_t i = 0; i < numColorAttachments; ++i)
{
[objCDesc.colorAttachments setObject:ToObjC(desc.colorAttachments[i])
atIndexedSubscript:i];
ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]);
}
for (uint32_t i = numColorAttachments; i < kMaxRenderTargets; ++i)
{
// Inactive render target
objCDesc.colorAttachments[i].texture = nil;
objCDesc.colorAttachments[i].level = 0;
objCDesc.colorAttachments[i].slice = 0;
objCDesc.colorAttachments[i].depthPlane = 0;
objCDesc.colorAttachments[i].loadAction = MTLLoadActionDontCare;
objCDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare;
}
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, depthAttachment);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stencilAttachment);
return objCDesc;
ToObjC(depthAttachment, objCDesc.depthAttachment);
ToObjC(stencilAttachment, objCDesc.stencilAttachment);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment