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 ...@@ -285,7 +285,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
mtl::ComputeCommandEncoder *getComputeCommandEncoder(); mtl::ComputeCommandEncoder *getComputeCommandEncoder();
private: private:
void ensureCommandBufferValid(); void ensureCommandBufferReady();
angle::Result ensureIncompleteTexturesCreated(const gl::Context *context); angle::Result ensureIncompleteTexturesCreated(const gl::Context *context);
angle::Result setupDraw(const gl::Context *context, angle::Result setupDraw(const gl::Context *context,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
......
...@@ -1087,7 +1087,7 @@ void ContextMtl::endEncoding(bool forceSaveRenderPassContent) ...@@ -1087,7 +1087,7 @@ void ContextMtl::endEncoding(bool forceSaveRenderPassContent)
void ContextMtl::flushCommandBufer() void ContextMtl::flushCommandBufer()
{ {
if (!mCmdBuffer.valid()) if (!mCmdBuffer.ready())
{ {
return; return;
} }
...@@ -1098,7 +1098,7 @@ void ContextMtl::flushCommandBufer() ...@@ -1098,7 +1098,7 @@ void ContextMtl::flushCommandBufer()
void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> presentationDrawable) 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: // Always discard default FBO's depth stencil buffers at the end of the frame:
if (mDrawFramebufferIsDefault && hasStartedRenderPass(mDrawFramebuffer)) if (mDrawFramebufferIsDefault && hasStartedRenderPass(mDrawFramebuffer))
...@@ -1121,10 +1121,7 @@ angle::Result ContextMtl::finishCommandBuffer() ...@@ -1121,10 +1121,7 @@ angle::Result ContextMtl::finishCommandBuffer()
{ {
flushCommandBufer(); flushCommandBufer();
if (mCmdBuffer.valid()) mCmdBuffer.finish();
{
mCmdBuffer.finish();
}
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1170,7 +1167,7 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::Render ...@@ -1170,7 +1167,7 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::Render
endEncoding(false); endEncoding(false);
ensureCommandBufferValid(); ensureCommandBufferReady();
// Need to re-apply everything on next draw call. // Need to re-apply everything on next draw call.
mDirtyBits.set(); mDirtyBits.set();
...@@ -1219,7 +1216,7 @@ mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoder() ...@@ -1219,7 +1216,7 @@ mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoder()
endEncoding(true); endEncoding(true);
ensureCommandBufferValid(); ensureCommandBufferReady();
return &mBlitEncoder.restart(); return &mBlitEncoder.restart();
} }
...@@ -1233,19 +1230,19 @@ mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoder() ...@@ -1233,19 +1230,19 @@ mtl::ComputeCommandEncoder *ContextMtl::getComputeCommandEncoder()
endEncoding(true); endEncoding(true);
ensureCommandBufferValid(); ensureCommandBufferReady();
return &mComputeEncoder.restart(); return &mComputeEncoder.restart();
} }
void ContextMtl::ensureCommandBufferValid() void ContextMtl::ensureCommandBufferReady()
{ {
if (!mCmdBuffer.valid()) if (!mCmdBuffer.ready())
{ {
mCmdBuffer.restart(); mCmdBuffer.restart();
} }
ASSERT(mCmdBuffer.valid()); ASSERT(mCmdBuffer.ready());
} }
void ContextMtl::updateViewport(FramebufferMtl *framebufferMtl, void ContextMtl::updateViewport(FramebufferMtl *framebufferMtl,
......
...@@ -693,21 +693,8 @@ angle::Result ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommand ...@@ -693,21 +693,8 @@ angle::Result ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommand
{ {
continue; continue;
} }
switch (shaderType) cmdEncoder->setBytes(shaderType, uniformBlock.uniformData.data(),
{ uniformBlock.uniformData.size(), mtl::kDefaultUniformsBindingIndex);
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();
}
mDefaultUniformBlocksDirty.reset(shaderType); mDefaultUniformBlocksDirty.reset(shaderType);
} }
...@@ -762,19 +749,8 @@ angle::Result ProgramMtl::updateTextures(const gl::Context *glContext, ...@@ -762,19 +749,8 @@ angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
TextureMtl *textureMtl = mtl::GetImpl(texture); TextureMtl *textureMtl = mtl::GetImpl(texture);
switch (shaderType) ANGLE_TRY(textureMtl->bindToShader(glContext, cmdEncoder, shaderType, textureSlot,
{ samplerSlot));
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();
}
} // for array elements } // for array elements
} // for sampler bindings } // for sampler bindings
} // for shader types } // for shader types
......
...@@ -150,14 +150,11 @@ class TextureMtl : public TextureImpl ...@@ -150,14 +150,11 @@ class TextureMtl : public TextureImpl
// to the actual texture. // to the actual texture.
angle::Result ensureTextureCreated(const gl::Context *context); angle::Result ensureTextureCreated(const gl::Context *context);
angle::Result bindVertexShader(const gl::Context *context, angle::Result bindToShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder, mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex, gl::ShaderType shaderType,
int samplerSlotIndex); int textureSlotIndex,
angle::Result bindFragmentShader(const gl::Context *context, int samplerSlotIndex);
mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex,
int samplerSlotIndex);
const mtl::Format &getFormat() const { return mFormat; } const mtl::Format &getFormat() const { return mFormat; }
......
...@@ -418,8 +418,8 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context) ...@@ -418,8 +418,8 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
{ {
encoder = contextMtl->getBlitCommandEncoder(); encoder = contextMtl->getBlitCommandEncoder();
} }
encoder->copyTexture(mNativeTexture, layer, mip, mtlOrigin, mtlSize, encoder->copyTexture(imageToTransfer, 0, 0, mtlOrigin, mtlSize, mNativeTexture,
imageToTransfer, 0, 0, mtlOrigin); layer, mip, mtlOrigin);
} }
imageToTransfer = nullptr; imageToTransfer = nullptr;
...@@ -828,42 +828,18 @@ angle::Result TextureMtl::syncState(const gl::Context *context, ...@@ -828,42 +828,18 @@ angle::Result TextureMtl::syncState(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result TextureMtl::bindVertexShader(const gl::Context *context, angle::Result TextureMtl::bindToShader(const gl::Context *context,
mtl::RenderCommandEncoder *cmdEncoder, mtl::RenderCommandEncoder *cmdEncoder,
int textureSlotIndex, gl::ShaderType shaderType,
int samplerSlotIndex) int textureSlotIndex,
int samplerSlotIndex)
{ {
ASSERT(mNativeTexture); 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; float maxLodClamp = FLT_MAX;
if (!mIsPow2)
{
maxLodClamp = 0;
}
cmdEncoder->setFragmentTexture(mNativeTexture, textureSlotIndex); cmdEncoder->setTexture(shaderType, mNativeTexture, textureSlotIndex);
cmdEncoder->setFragmentSamplerState(mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex); cmdEncoder->setSamplerState(shaderType, mMetalSamplerState, 0, maxLodClamp, samplerSlotIndex);
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -59,6 +59,9 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non ...@@ -59,6 +59,9 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non
} }
bool isResourceBeingUsedByGPU(const Resource *resource) const; 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) CommandQueue &operator=(id<MTLCommandQueue> metalQueue)
{ {
set(metalQueue); set(metalQueue);
...@@ -66,6 +69,7 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non ...@@ -66,6 +69,7 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non
} }
AutoObjCPtr<id<MTLCommandBuffer>> makeMetalCommandBuffer(uint64_t *queueSerialOut); AutoObjCPtr<id<MTLCommandBuffer>> makeMetalCommandBuffer(uint64_t *queueSerialOut);
void onCommandBufferCommitted(id<MTLCommandBuffer> buf, uint64_t serial);
private: private:
void onCommandBufferCompleted(id<MTLCommandBuffer> buf, uint64_t serial); void onCommandBufferCompleted(id<MTLCommandBuffer> buf, uint64_t serial);
...@@ -80,6 +84,7 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non ...@@ -80,6 +84,7 @@ class CommandQueue final : public WrappedObject<id<MTLCommandQueue>>, angle::Non
std::deque<CmdBufferQueueEntry> mMetalCmdBuffersTmp; std::deque<CmdBufferQueueEntry> mMetalCmdBuffersTmp;
uint64_t mQueueSerialCounter = 1; uint64_t mQueueSerialCounter = 1;
std::atomic<uint64_t> mCommittedBufferSerial{0};
std::atomic<uint64_t> mCompletedBufferSerial{0}; std::atomic<uint64_t> mCompletedBufferSerial{0};
mutable std::mutex mLock; mutable std::mutex mLock;
...@@ -91,10 +96,14 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N ...@@ -91,10 +96,14 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
CommandBuffer(CommandQueue *cmdQueue); CommandBuffer(CommandQueue *cmdQueue);
~CommandBuffer(); ~CommandBuffer();
// This method must be called so that command encoder can be used.
void restart(); 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(); void commit();
// wait for committed command buffer to finish.
void finish(); void finish();
void present(id<CAMetalDrawable> presentationDrawable); void present(id<CAMetalDrawable> presentationDrawable);
...@@ -104,6 +113,7 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N ...@@ -104,6 +113,7 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
CommandQueue &cmdQueue() { return mCmdQueue; } CommandQueue &cmdQueue() { return mCmdQueue; }
// Private use only
void setActiveCommandEncoder(CommandEncoder *encoder); void setActiveCommandEncoder(CommandEncoder *encoder);
void invalidateActiveCommandEncoder(CommandEncoder *encoder); void invalidateActiveCommandEncoder(CommandEncoder *encoder);
...@@ -111,14 +121,15 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N ...@@ -111,14 +121,15 @@ class CommandBuffer final : public WrappedObject<id<MTLCommandBuffer>>, angle::N
void set(id<MTLCommandBuffer> metalBuffer); void set(id<MTLCommandBuffer> metalBuffer);
void cleanup(); void cleanup();
bool validImpl() const; bool readyImpl() const;
void commitImpl(); void commitImpl();
void forceEndingCurrentEncoder();
using ParentClass = WrappedObject<id<MTLCommandBuffer>>; using ParentClass = WrappedObject<id<MTLCommandBuffer>>;
CommandQueue &mCmdQueue; CommandQueue &mCmdQueue;
std::atomic<CommandEncoder *> mActiveCommandEncoder{nullptr}; CommandEncoder *mActiveCommandEncoder = nullptr;
uint64_t mQueueSerial = 0; uint64_t mQueueSerial = 0;
...@@ -141,7 +152,7 @@ class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>>, angle::NonCo ...@@ -141,7 +152,7 @@ class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>>, angle::NonCo
virtual void endEncoding(); virtual void endEncoding();
void reset(); virtual void reset();
Type getType() const { return mType; } Type getType() const { return mType; }
CommandEncoder &markResourceBeingWrittenByGPU(const BufferRef &buffer); CommandEncoder &markResourceBeingWrittenByGPU(const BufferRef &buffer);
...@@ -162,14 +173,123 @@ class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>>, angle::NonCo ...@@ -162,14 +173,123 @@ class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>>, angle::NonCo
CommandBuffer &mCmdBuffer; 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 class RenderCommandEncoder final : public CommandEncoder
{ {
public: public:
RenderCommandEncoder(CommandBuffer *cmdBuffer); RenderCommandEncoder(CommandBuffer *cmdBuffer);
~RenderCommandEncoder() override; ~RenderCommandEncoder() override;
// override CommandEncoder
bool valid() const { return mRecording; }
void reset() override;
void endEncoding() 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 &restart(const RenderPassDesc &desc);
RenderCommandEncoder &setRenderPipelineState(id<MTLRenderPipelineState> state); RenderCommandEncoder &setRenderPipelineState(id<MTLRenderPipelineState> state);
...@@ -187,8 +307,14 @@ class RenderCommandEncoder final : public CommandEncoder ...@@ -187,8 +307,14 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setBlendColor(float r, float g, float b, float a); RenderCommandEncoder &setBlendColor(float r, float g, float b, float a);
RenderCommandEncoder &setVertexBuffer(const BufferRef &buffer, uint32_t offset, uint32_t index); RenderCommandEncoder &setVertexBuffer(const BufferRef &buffer, uint32_t offset, uint32_t index)
RenderCommandEncoder &setVertexBytes(const uint8_t *bytes, size_t size, 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> template <typename T>
RenderCommandEncoder &setVertexData(const T &data, uint32_t index) RenderCommandEncoder &setVertexData(const T &data, uint32_t index)
{ {
...@@ -197,13 +323,25 @@ class RenderCommandEncoder final : public CommandEncoder ...@@ -197,13 +323,25 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setVertexSamplerState(id<MTLSamplerState> state, RenderCommandEncoder &setVertexSamplerState(id<MTLSamplerState> state,
float lodMinClamp, float lodMinClamp,
float lodMaxClamp, float lodMaxClamp,
uint32_t index); uint32_t index)
RenderCommandEncoder &setVertexTexture(const TextureRef &texture, 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, RenderCommandEncoder &setFragmentBuffer(const BufferRef &buffer,
uint32_t offset, uint32_t offset,
uint32_t index); uint32_t index)
RenderCommandEncoder &setFragmentBytes(const uint8_t *bytes, size_t size, 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> template <typename T>
RenderCommandEncoder &setFragmentData(const T &data, uint32_t index) RenderCommandEncoder &setFragmentData(const T &data, uint32_t index)
{ {
...@@ -212,8 +350,40 @@ class RenderCommandEncoder final : public CommandEncoder ...@@ -212,8 +350,40 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setFragmentSamplerState(id<MTLSamplerState> state, RenderCommandEncoder &setFragmentSamplerState(id<MTLSamplerState> state,
float lodMinClamp, float lodMinClamp,
float lodMaxClamp, float lodMaxClamp,
uint32_t index); uint32_t index)
RenderCommandEncoder &setFragmentTexture(const TextureRef &texture, 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, RenderCommandEncoder &draw(MTLPrimitiveType primitiveType,
uint32_t vertexStart, uint32_t vertexStart,
...@@ -250,20 +420,51 @@ class RenderCommandEncoder final : public CommandEncoder ...@@ -250,20 +420,51 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setDepthStoreAction(MTLStoreAction action); RenderCommandEncoder &setDepthStoreAction(MTLStoreAction action);
RenderCommandEncoder &setStencilStoreAction(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; } const RenderPassDesc &renderPassDesc() const { return mRenderPassDesc; }
bool hasDrawCalls() const { return mHasDrawCalls; }
private: private:
// Override CommandEncoder
id<MTLRenderCommandEncoder> get() id<MTLRenderCommandEncoder> get()
{ {
return static_cast<id<MTLRenderCommandEncoder>>(CommandEncoder::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; RenderPassDesc mRenderPassDesc;
MTLStoreAction mColorInitialStoreActions[kMaxRenderTargets]; // Cached Objective-C render pass desc to avoid re-allocate every frame.
MTLStoreAction mDepthInitialStoreAction; mtl::AutoObjCObj<MTLRenderPassDescriptor> mCachedRenderPassDescObjC;
MTLStoreAction mStencilInitialStoreAction; 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 class BlitCommandEncoder final : public CommandEncoder
...@@ -272,6 +473,8 @@ class BlitCommandEncoder final : public CommandEncoder ...@@ -272,6 +473,8 @@ class BlitCommandEncoder final : public CommandEncoder
BlitCommandEncoder(CommandBuffer *cmdBuffer); BlitCommandEncoder(CommandBuffer *cmdBuffer);
~BlitCommandEncoder() override; ~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 &restart();
BlitCommandEncoder &copyBufferToTexture(const BufferRef &src, BlitCommandEncoder &copyBufferToTexture(const BufferRef &src,
...@@ -285,15 +488,15 @@ class BlitCommandEncoder final : public CommandEncoder ...@@ -285,15 +488,15 @@ class BlitCommandEncoder final : public CommandEncoder
MTLOrigin dstOrigin, MTLOrigin dstOrigin,
MTLBlitOption blitOption); MTLBlitOption blitOption);
BlitCommandEncoder &copyTexture(const TextureRef &dst, BlitCommandEncoder &copyTexture(const TextureRef &src,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin,
MTLSize dstSize,
const TextureRef &src,
uint32_t srcSlice, uint32_t srcSlice,
uint32_t srcLevel, 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 &generateMipmapsForTexture(const TextureRef &texture);
BlitCommandEncoder &synchronizeResource(const TextureRef &texture); BlitCommandEncoder &synchronizeResource(const TextureRef &texture);
...@@ -311,11 +514,16 @@ class ComputeCommandEncoder final : public CommandEncoder ...@@ -311,11 +514,16 @@ class ComputeCommandEncoder final : public CommandEncoder
ComputeCommandEncoder(CommandBuffer *cmdBuffer); ComputeCommandEncoder(CommandBuffer *cmdBuffer);
~ComputeCommandEncoder() override; ~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 &restart();
ComputeCommandEncoder &setComputePipelineState(id<MTLComputePipelineState> state); ComputeCommandEncoder &setComputePipelineState(id<MTLComputePipelineState> state);
ComputeCommandEncoder &setBuffer(const BufferRef &buffer, uint32_t offset, uint32_t index); 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); ComputeCommandEncoder &setBytes(const uint8_t *bytes, size_t size, uint32_t index);
template <typename T> template <typename T>
ComputeCommandEncoder &setData(const T &data, uint32_t index) ComputeCommandEncoder &setData(const T &data, uint32_t index)
...@@ -327,10 +535,13 @@ class ComputeCommandEncoder final : public CommandEncoder ...@@ -327,10 +535,13 @@ class ComputeCommandEncoder final : public CommandEncoder
float lodMaxClamp, float lodMaxClamp,
uint32_t index); uint32_t index);
ComputeCommandEncoder &setTexture(const TextureRef &texture, 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: private:
id<MTLComputeCommandEncoder> get() id<MTLComputeCommandEncoder> get()
......
...@@ -15,11 +15,314 @@ ...@@ -15,11 +15,314 @@
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/renderer/metal/mtl_resources.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 rx
{ {
namespace mtl 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 // CommandQueue implementation
void CommandQueue::reset() void CommandQueue::reset()
{ {
...@@ -97,7 +400,18 @@ bool CommandQueue::isResourceBeingUsedByGPU(const Resource *resource) const ...@@ -97,7 +400,18 @@ bool CommandQueue::isResourceBeingUsedByGPU(const Resource *resource) const
} }
return mCompletedBufferSerial.load(std::memory_order_relaxed) < 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) AutoObjCPtr<id<MTLCommandBuffer>> CommandQueue::makeMetalCommandBuffer(uint64_t *queueSerialOut)
...@@ -128,6 +442,17 @@ AutoObjCPtr<id<MTLCommandBuffer>> CommandQueue::makeMetalCommandBuffer(uint64_t ...@@ -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) void CommandQueue::onCommandBufferCompleted(id<MTLCommandBuffer> buf, uint64_t serial)
{ {
std::lock_guard<std::mutex> lg(mLock); std::lock_guard<std::mutex> lg(mLock);
...@@ -164,11 +489,11 @@ CommandBuffer::~CommandBuffer() ...@@ -164,11 +489,11 @@ CommandBuffer::~CommandBuffer()
cleanup(); cleanup();
} }
bool CommandBuffer::valid() const bool CommandBuffer::ready() const
{ {
std::lock_guard<std::mutex> lg(mLock); std::lock_guard<std::mutex> lg(mLock);
return validImpl(); return readyImpl();
} }
void CommandBuffer::commit() void CommandBuffer::commit()
...@@ -197,7 +522,7 @@ void CommandBuffer::setWriteDependency(const ResourceRef &resource) ...@@ -197,7 +522,7 @@ void CommandBuffer::setWriteDependency(const ResourceRef &resource)
std::lock_guard<std::mutex> lg(mLock); std::lock_guard<std::mutex> lg(mLock);
if (!validImpl()) if (!readyImpl())
{ {
return; return;
} }
...@@ -214,7 +539,7 @@ void CommandBuffer::setReadDependency(const ResourceRef &resource) ...@@ -214,7 +539,7 @@ void CommandBuffer::setReadDependency(const ResourceRef &resource)
std::lock_guard<std::mutex> lg(mLock); std::lock_guard<std::mutex> lg(mLock);
if (!validImpl()) if (!readyImpl())
{ {
return; return;
} }
...@@ -249,7 +574,10 @@ void CommandBuffer::setActiveCommandEncoder(CommandEncoder *encoder) ...@@ -249,7 +574,10 @@ void CommandBuffer::setActiveCommandEncoder(CommandEncoder *encoder)
void CommandBuffer::invalidateActiveCommandEncoder(CommandEncoder *encoder) void CommandBuffer::invalidateActiveCommandEncoder(CommandEncoder *encoder)
{ {
mActiveCommandEncoder.compare_exchange_strong(encoder, nullptr); if (mActiveCommandEncoder == encoder)
{
mActiveCommandEncoder = nullptr;
}
} }
void CommandBuffer::cleanup() void CommandBuffer::cleanup()
...@@ -259,7 +587,7 @@ void CommandBuffer::cleanup() ...@@ -259,7 +587,7 @@ void CommandBuffer::cleanup()
ParentClass::set(nil); ParentClass::set(nil);
} }
bool CommandBuffer::validImpl() const bool CommandBuffer::readyImpl() const
{ {
if (!ParentClass::valid()) if (!ParentClass::valid())
{ {
...@@ -271,26 +599,32 @@ bool CommandBuffer::validImpl() const ...@@ -271,26 +599,32 @@ bool CommandBuffer::validImpl() const
void CommandBuffer::commitImpl() void CommandBuffer::commitImpl()
{ {
if (!validImpl()) if (!readyImpl())
{ {
return; return;
} }
// End the current encoder // End the current encoder
if (mActiveCommandEncoder.load(std::memory_order_relaxed)) forceEndingCurrentEncoder();
{
mActiveCommandEncoder.load(std::memory_order_relaxed)->endEncoding(); // Notify command queue
mActiveCommandEncoder = nullptr; mCmdQueue.onCommandBufferCommitted(get(), mQueueSerial);
}
// Do the actual commit // Do the actual commit
[get() commit]; [get() commit];
ANGLE_MTL_LOG("Committed MTLCommandBuffer %llu:%p", mQueueSerial, get());
mCommitted = true; mCommitted = true;
} }
void CommandBuffer::forceEndingCurrentEncoder()
{
if (mActiveCommandEncoder)
{
mActiveCommandEncoder->endEncoding();
mActiveCommandEncoder = nullptr;
}
}
// CommandEncoder implementation // CommandEncoder implementation
CommandEncoder::CommandEncoder(CommandBuffer *cmdBuffer, Type type) CommandEncoder::CommandEncoder(CommandBuffer *cmdBuffer, Type type)
: mType(type), mCmdBuffer(*cmdBuffer) : mType(type), mCmdBuffer(*cmdBuffer)
...@@ -334,74 +668,207 @@ CommandEncoder &CommandEncoder::markResourceBeingWrittenByGPU(const TextureRef & ...@@ -334,74 +668,207 @@ CommandEncoder &CommandEncoder::markResourceBeingWrittenByGPU(const TextureRef &
return *this; return *this;
} }
// RenderCommandEncoder implemtation // RenderCommandEncoderShaderStates implementation
RenderCommandEncoder::RenderCommandEncoder(CommandBuffer *cmdBuffer) RenderCommandEncoderShaderStates::RenderCommandEncoderShaderStates()
: CommandEncoder(cmdBuffer, RENDER) {
{} reset();
RenderCommandEncoder::~RenderCommandEncoder() {} }
void RenderCommandEncoder::endEncoding() void RenderCommandEncoderShaderStates::reset()
{ {
if (!valid()) for (id<MTLBuffer> &buffer : buffers)
return; {
buffer = nil;
}
// Now is the time to do the actual store option setting. for (uint32_t &offset : bufferOffsets)
auto metalEncoder = get();
for (uint32_t i = 0; i < mRenderPassDesc.numColorAttachments; ++i)
{ {
if (mRenderPassDesc.colorAttachments[i].storeAction == MTLStoreActionUnknown) offset = 0;
{ }
// If storeAction hasn't been set for this attachment, we set to dontcare.
mRenderPassDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare;
}
// Only initial unknown store action can change the value now. for (id<MTLSamplerState> &sampler : samplers)
if (mColorInitialStoreActions[i] == MTLStoreActionUnknown) {
{ sampler = nil;
[metalEncoder setColorStoreAction:mRenderPassDesc.colorAttachments[i].storeAction
atIndex:i];
}
} }
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. lodClampRange.reset();
mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionDontCare;
} }
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. // 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(); CommandEncoder::endEncoding();
// reset state // reset state
mRenderPassDesc = RenderPassDesc(); mRenderPassDesc = RenderPassDesc();
mStateCache.reset();
} }
inline void RenderCommandEncoder::initWriteDependencyAndStoreAction(const TextureRef &texture, inline void RenderCommandEncoder::initAttachmentWriteDependencyAndScissorRect(
MTLStoreAction *storeActionOut) const RenderPassAttachmentDesc &attachment)
{ {
TextureRef texture = attachment.texture;
if (texture) if (texture)
{ {
cmdBuffer().setWriteDependency(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 ANGLE_MTL_LOG("Creating new render command encoder with desc: %@",
*storeActionOut = MTLStoreActionDontCare; 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) ...@@ -419,86 +886,102 @@ RenderCommandEncoder &RenderCommandEncoder::restart(const RenderPassDesc &desc)
endEncoding(); endEncoding();
} }
if (!cmdBuffer().valid()) if (!cmdBuffer().ready())
{ {
reset(); reset();
return *this; 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 initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.colorAttachments[i]);
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());
id<MTLRenderCommandEncoder> metalCmdEncoder = initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.depthAttachment);
[cmdBuffer().get() renderCommandEncoderWithDescriptor:objCDesc];
set(metalCmdEncoder); initAttachmentWriteDependencyAndScissorRect(mRenderPassDesc.stencilAttachment);
// Set the actual store action // Convert to Objective-C descriptor
for (uint32_t i = 0; i < desc.numColorAttachments; ++i) mRenderPassDesc.convertToMetalDesc(mCachedRenderPassDescObjC);
{
setColorStoreAction(desc.colorAttachments[i].storeAction, i);
}
setDepthStencilStoreAction(desc.depthAttachment.storeAction, // The actual Objective-C encoder will be created later in endEncoding(), we do so in
desc.stencilAttachment.storeAction); // order to be able to sort the commands or do the preprocessing before the actual
// encoding.
// Verify that it was created successfully // Since we defer the native encoder creation, we need to explicitly tell command buffer
ASSERT(get()); // that this object is the active encoder:
} // ANGLE_MTL_OBJC_SCOPE cmdBuffer().setActiveCommandEncoder(this);
return *this; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setRenderPipelineState(id<MTLRenderPipelineState> state) 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setTriangleFillMode(MTLTriangleFillMode mode) 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setFrontFacingWinding(MTLWinding winding) 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setCullMode(MTLCullMode mode) 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setDepthStencilState(id<MTLDepthStencilState> state) 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; return *this;
} }
...@@ -506,7 +989,16 @@ RenderCommandEncoder &RenderCommandEncoder::setDepthBias(float depthBias, ...@@ -506,7 +989,16 @@ RenderCommandEncoder &RenderCommandEncoder::setDepthBias(float depthBias,
float slopeScale, float slopeScale,
float clamp) 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; return *this;
} }
...@@ -515,7 +1007,15 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilRefVals(uint32_t frontRef, ...@@ -515,7 +1007,15 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilRefVals(uint32_t frontRef,
// Metal has some bugs when reference values are larger than 0xff // Metal has some bugs when reference values are larger than 0xff
ASSERT(frontRef == (frontRef & kStencilMaskAll)); ASSERT(frontRef == (frontRef & kStencilMaskAll));
ASSERT(backRef == (backRef & 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; return *this;
} }
...@@ -527,28 +1027,61 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilRefVal(uint32_t ref) ...@@ -527,28 +1027,61 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilRefVal(uint32_t ref)
RenderCommandEncoder &RenderCommandEncoder::setViewport(const MTLViewport &viewport) 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setScissorRect(const MTLScissorRect &rect) 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setBlendColor(float r, float g, float b, float a) 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setVertexBuffer(const BufferRef &buffer, RenderCommandEncoder &RenderCommandEncoder::setBuffer(gl::ShaderType shaderType,
uint32_t offset, const BufferRef &buffer,
uint32_t index) uint32_t offset,
uint32_t index)
{ {
if (index >= kMaxShaderBuffers) if (index >= kMaxShaderBuffers)
{ {
...@@ -557,113 +1090,140 @@ RenderCommandEncoder &RenderCommandEncoder::setVertexBuffer(const BufferRef &buf ...@@ -557,113 +1090,140 @@ RenderCommandEncoder &RenderCommandEncoder::setVertexBuffer(const BufferRef &buf
cmdBuffer().setReadDependency(buffer); 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, RenderCommandEncoder &RenderCommandEncoder::setBufferForWrite(gl::ShaderType shaderType,
size_t size, const BufferRef &buffer,
uint32_t index) uint32_t offset,
uint32_t index)
{ {
if (index >= kMaxShaderBuffers) if (index >= kMaxShaderBuffers)
{ {
return *this; 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, RenderCommandEncoder &RenderCommandEncoder::commonSetBuffer(gl::ShaderType shaderType,
float lodMinClamp, id<MTLBuffer> mtlBuffer,
float lodMaxClamp, uint32_t offset,
uint32_t index) 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 // If buffer already bound but with different offset, then update the offset only.
lodMinClamp:lodMinClamp shaderStates.bufferOffsets[index] = offset;
lodMaxClamp:lodMaxClamp
atIndex:index]; 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; return *this;
} }
cmdBuffer().setReadDependency(texture); shaderStates.buffers[index] = mtlBuffer;
[get() setVertexTexture:(texture ? texture->get() : nil) atIndex:index]; shaderStates.bufferOffsets[index] = offset;
mCommands.push(static_cast<CmdType>(mSetBufferCmds[shaderType]))
.push([mtlBuffer ANGLE_MTL_RETAIN])
.push(offset)
.push(index);
return *this; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setFragmentBuffer(const BufferRef &buffer, RenderCommandEncoder &RenderCommandEncoder::setBytes(gl::ShaderType shaderType,
uint32_t offset, const uint8_t *bytes,
uint32_t index) size_t size,
uint32_t index)
{ {
if (index >= kMaxShaderBuffers) if (index >= kMaxShaderBuffers)
{ {
return *this; 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setFragmentBytes(const uint8_t *bytes, RenderCommandEncoder &RenderCommandEncoder::setSamplerState(gl::ShaderType shaderType,
size_t size, id<MTLSamplerState> state,
uint32_t index) float lodMinClamp,
float lodMaxClamp,
uint32_t index)
{ {
if (index >= kMaxShaderBuffers) if (index >= kMaxShaderSamplers)
{ {
return *this; 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; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setTexture(gl::ShaderType shaderType,
RenderCommandEncoder &RenderCommandEncoder::setFragmentSamplerState(id<MTLSamplerState> state, const TextureRef &texture,
float lodMinClamp, uint32_t index)
float lodMaxClamp,
uint32_t index)
{ {
if (index >= kMaxShaderSamplers) if (index >= kMaxShaderSamplers)
{ {
return *this; return *this;
} }
[get() setFragmentSamplerState:state cmdBuffer().setReadDependency(texture);
lodMinClamp:lodMinClamp
lodMaxClamp:lodMaxClamp
atIndex:index];
return *this; id<MTLTexture> mtlTexture = (texture ? texture->get() : nil);
}
RenderCommandEncoder &RenderCommandEncoder::setFragmentTexture(const TextureRef &texture, RenderCommandEncoderShaderStates &shaderStates = mStateCache.perShaderStates[shaderType];
uint32_t index) if (shaderStates.textures[index] == mtlTexture)
{
if (index >= kMaxShaderSamplers)
{ {
return *this; return *this;
} }
shaderStates.textures[index] = mtlTexture;
cmdBuffer().setReadDependency(texture); mCommands.push(static_cast<CmdType>(mSetTextureCmds[shaderType]))
[get() setFragmentTexture:(texture ? texture->get() : nil) atIndex:index]; .push([mtlTexture ANGLE_MTL_RETAIN])
.push(index);
return *this; return *this;
} }
...@@ -672,7 +1232,14 @@ RenderCommandEncoder &RenderCommandEncoder::draw(MTLPrimitiveType primitiveType, ...@@ -672,7 +1232,14 @@ RenderCommandEncoder &RenderCommandEncoder::draw(MTLPrimitiveType primitiveType,
uint32_t vertexStart, uint32_t vertexStart,
uint32_t vertexCount) 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; return *this;
} }
...@@ -682,10 +1249,18 @@ RenderCommandEncoder &RenderCommandEncoder::drawInstanced(MTLPrimitiveType primi ...@@ -682,10 +1249,18 @@ RenderCommandEncoder &RenderCommandEncoder::drawInstanced(MTLPrimitiveType primi
uint32_t vertexCount, uint32_t vertexCount,
uint32_t instances) uint32_t instances)
{ {
[get() drawPrimitives:primitiveType if (ANGLE_UNLIKELY(!mStateCache.renderPipeline))
vertexStart:vertexStart {
vertexCount:vertexCount // Ignore draw call if there is no render pipeline state set prior to this.
instanceCount:instances]; return *this;
}
mHasDrawCalls = true;
mCommands.push(CmdType::DrawInstanced)
.push(primitiveType)
.push(vertexStart)
.push(vertexCount)
.push(instances);
return *this; return *this;
} }
...@@ -696,17 +1271,26 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primiti ...@@ -696,17 +1271,26 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primiti
const BufferRef &indexBuffer, const BufferRef &indexBuffer,
size_t bufferOffset) 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; return *this;
} }
if (ANGLE_UNLIKELY(!indexBuffer))
{
return *this;
}
mHasDrawCalls = true;
cmdBuffer().setReadDependency(indexBuffer); cmdBuffer().setReadDependency(indexBuffer);
[get() drawIndexedPrimitives:primitiveType
indexCount:indexCount mCommands.push(CmdType::DrawIndexed)
indexType:indexType .push(primitiveType)
indexBuffer:indexBuffer->get() .push(indexCount)
indexBufferOffset:bufferOffset]; .push(indexType)
.push([indexBuffer->get() ANGLE_MTL_RETAIN])
.push(bufferOffset);
return *this; return *this;
} }
...@@ -718,18 +1302,27 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstanced(MTLPrimitiveTyp ...@@ -718,18 +1302,27 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstanced(MTLPrimitiveTyp
size_t bufferOffset, size_t bufferOffset,
uint32_t instances) 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; return *this;
} }
mHasDrawCalls = true;
cmdBuffer().setReadDependency(indexBuffer); cmdBuffer().setReadDependency(indexBuffer);
[get() drawIndexedPrimitives:primitiveType
indexCount:indexCount mCommands.push(CmdType::DrawIndexedInstanced)
indexType:indexType .push(primitiveType)
indexBuffer:indexBuffer->get() .push(indexCount)
indexBufferOffset:bufferOffset .push(indexType)
instanceCount:instances]; .push([indexBuffer->get() ANGLE_MTL_RETAIN])
.push(bufferOffset)
.push(instances);
return *this; return *this;
} }
...@@ -743,20 +1336,28 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstancedBaseVertex( ...@@ -743,20 +1336,28 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstancedBaseVertex(
uint32_t instances, uint32_t instances,
uint32_t baseVertex) 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; return *this;
} }
mHasDrawCalls = true;
cmdBuffer().setReadDependency(indexBuffer); cmdBuffer().setReadDependency(indexBuffer);
[get() drawIndexedPrimitives:primitiveType
indexCount:indexCount mCommands.push(CmdType::DrawIndexedInstancedBaseVertex)
indexType:indexType .push(primitiveType)
indexBuffer:indexBuffer->get() .push(indexCount)
indexBufferOffset:bufferOffset .push(indexType)
instanceCount:instances .push([indexBuffer->get() ANGLE_MTL_RETAIN])
baseVertex:baseVertex .push(bufferOffset)
baseInstance:0]; .push(instances)
.push(baseVertex);
return *this; return *this;
} }
...@@ -811,6 +1412,44 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilStoreAction(MTLStoreAction ...@@ -811,6 +1412,44 @@ RenderCommandEncoder &RenderCommandEncoder::setStencilStoreAction(MTLStoreAction
return *this; 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::BlitCommandEncoder(CommandBuffer *cmdBuffer) : CommandEncoder(cmdBuffer, BLIT) BlitCommandEncoder::BlitCommandEncoder(CommandBuffer *cmdBuffer) : CommandEncoder(cmdBuffer, BLIT)
{} {}
...@@ -827,7 +1466,7 @@ BlitCommandEncoder &BlitCommandEncoder::restart() ...@@ -827,7 +1466,7 @@ BlitCommandEncoder &BlitCommandEncoder::restart()
return *this; return *this;
} }
if (!cmdBuffer().valid()) if (!cmdBuffer().ready())
{ {
reset(); reset();
return *this; return *this;
...@@ -876,15 +1515,15 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src ...@@ -876,15 +1515,15 @@ BlitCommandEncoder &BlitCommandEncoder::copyBufferToTexture(const BufferRef &src
return *this; return *this;
} }
BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &dst, BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &src,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin,
MTLSize dstSize,
const TextureRef &src,
uint32_t srcSlice, uint32_t srcSlice,
uint32_t srcLevel, uint32_t srcLevel,
MTLOrigin srcOrigin) MTLOrigin srcOrigin,
MTLSize srcSize,
const TextureRef &dst,
uint32_t dstSlice,
uint32_t dstLevel,
MTLOrigin dstOrigin)
{ {
if (!src || !dst) if (!src || !dst)
{ {
...@@ -897,7 +1536,7 @@ BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &dst, ...@@ -897,7 +1536,7 @@ BlitCommandEncoder &BlitCommandEncoder::copyTexture(const TextureRef &dst,
sourceSlice:srcSlice sourceSlice:srcSlice
sourceLevel:srcLevel sourceLevel:srcLevel
sourceOrigin:srcOrigin sourceOrigin:srcOrigin
sourceSize:dstSize sourceSize:srcSize
toTexture:dst->get() toTexture:dst->get()
destinationSlice:dstSlice destinationSlice:dstSlice
destinationLevel:dstLevel destinationLevel:dstLevel
...@@ -928,8 +1567,15 @@ BlitCommandEncoder &BlitCommandEncoder::synchronizeResource(const TextureRef &te ...@@ -928,8 +1567,15 @@ BlitCommandEncoder &BlitCommandEncoder::synchronizeResource(const TextureRef &te
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
// Only MacOS has separated storage for resource on CPU and GPU and needs explicit // Only MacOS has separated storage for resource on CPU and GPU and needs explicit
// synchronization // synchronization
cmdBuffer().setWriteDependency(texture); cmdBuffer().setReadDependency(texture);
[get() synchronizeResource:texture->get()]; if (texture->get().parentTexture)
{
[get() synchronizeResource:texture->get().parentTexture];
}
else
{
[get() synchronizeResource:texture->get()];
}
#endif #endif
return *this; return *this;
} }
...@@ -950,7 +1596,7 @@ ComputeCommandEncoder &ComputeCommandEncoder::restart() ...@@ -950,7 +1596,7 @@ ComputeCommandEncoder &ComputeCommandEncoder::restart()
return *this; return *this;
} }
if (!cmdBuffer().valid()) if (!cmdBuffer().ready())
{ {
reset(); reset();
return *this; return *this;
...@@ -982,15 +1628,26 @@ ComputeCommandEncoder &ComputeCommandEncoder::setBuffer(const BufferRef &buffer, ...@@ -982,15 +1628,26 @@ ComputeCommandEncoder &ComputeCommandEncoder::setBuffer(const BufferRef &buffer,
return *this; return *this;
} }
// NOTE(hqle): Assume compute shader both reads and writes to this buffer for now.
cmdBuffer().setReadDependency(buffer); cmdBuffer().setReadDependency(buffer);
cmdBuffer().setWriteDependency(buffer);
[get() setBuffer:(buffer ? buffer->get() : nil) offset:offset atIndex:index]; [get() setBuffer:(buffer ? buffer->get() : nil) offset:offset atIndex:index];
return *this; 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, ComputeCommandEncoder &ComputeCommandEncoder::setBytes(const uint8_t *bytes,
size_t size, size_t size,
uint32_t index) uint32_t index)
...@@ -1026,27 +1683,39 @@ ComputeCommandEncoder &ComputeCommandEncoder::setTexture(const TextureRef &textu ...@@ -1026,27 +1683,39 @@ ComputeCommandEncoder &ComputeCommandEncoder::setTexture(const TextureRef &textu
return *this; return *this;
} }
// NOTE(hqle): Assume compute shader both reads and writes to this texture for now.
cmdBuffer().setReadDependency(texture); cmdBuffer().setReadDependency(texture);
cmdBuffer().setWriteDependency(texture);
[get() setTexture:(texture ? texture->get() : nil) atIndex:index]; [get() setTexture:(texture ? texture->get() : nil) atIndex:index];
return *this; 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, ComputeCommandEncoder &ComputeCommandEncoder::dispatch(const MTLSize &threadGroupsPerGrid,
MTLSize threadsPerGroup) const MTLSize &threadsPerGroup)
{ {
[get() dispatchThreadgroups:threadGroupsPerGrid threadsPerThreadgroup:threadsPerGroup]; [get() dispatchThreadgroups:threadGroupsPerGrid threadsPerThreadgroup:threadsPerGroup];
return *this; return *this;
} }
ComputeCommandEncoder &ComputeCommandEncoder::dispatchNonUniform(MTLSize threadsPerGrid, ComputeCommandEncoder &ComputeCommandEncoder::dispatchNonUniform(const MTLSize &threadsPerGrid,
MTLSize threadsPerGroup) const MTLSize &threadsPerGroup)
{ {
#if TARGET_OS_TV
UNREACHABLE();
#else
[get() dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerGroup]; [get() dispatchThreads:threadsPerGrid threadsPerThreadgroup:threadsPerGroup];
#endif
return *this; return *this;
} }
} }
} }
...@@ -35,8 +35,12 @@ ...@@ -35,8 +35,12 @@
#if !__has_feature(objc_arc) #if !__has_feature(objc_arc)
# define ANGLE_MTL_AUTORELEASE autorelease # define ANGLE_MTL_AUTORELEASE autorelease
# define ANGLE_MTL_RETAIN retain
# define ANGLE_MTL_RELEASE release
#else #else
# define ANGLE_MTL_AUTORELEASE self # define ANGLE_MTL_AUTORELEASE self
# define ANGLE_MTL_RETAIN self
# define ANGLE_MTL_RELEASE self
#endif #endif
#define ANGLE_MTL_UNUSED __attribute__((unused)) #define ANGLE_MTL_UNUSED __attribute__((unused))
......
...@@ -846,7 +846,7 @@ angle::Result IndexGeneratorUtils::convertIndexBufferGPU(ContextMtl *contextMtl, ...@@ -846,7 +846,7 @@ angle::Result IndexGeneratorUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
cmdEncoder->setData(uniform, 0); cmdEncoder->setData(uniform, 0);
cmdEncoder->setBuffer(params.srcBuffer, 0, 1); 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); DispatchCompute(contextMtl, cmdEncoder, pipelineState, params.indexCount);
...@@ -878,7 +878,7 @@ angle::Result IndexGeneratorUtils::generateTriFanBufferFromArrays( ...@@ -878,7 +878,7 @@ angle::Result IndexGeneratorUtils::generateTriFanBufferFromArrays(
uniform.vertexCountFrom3rd = params.vertexCount - 2; uniform.vertexCountFrom3rd = params.vertexCount - 2;
cmdEncoder->setData(uniform, 0); cmdEncoder->setData(uniform, 0);
cmdEncoder->setBuffer(params.dstBuffer, params.dstOffset, 2); cmdEncoder->setBufferForWrite(params.dstBuffer, params.dstOffset, 2);
DispatchCompute(contextMtl, cmdEncoder, mTriFanFromArraysGeneratorPipeline, DispatchCompute(contextMtl, cmdEncoder, mTriFanFromArraysGeneratorPipeline,
uniform.vertexCountFrom3rd); uniform.vertexCountFrom3rd);
...@@ -937,7 +937,7 @@ angle::Result IndexGeneratorUtils::generateTriFanBufferFromElementsArrayGPU( ...@@ -937,7 +937,7 @@ angle::Result IndexGeneratorUtils::generateTriFanBufferFromElementsArrayGPU(
cmdEncoder->setData(uniform, 0); cmdEncoder->setData(uniform, 0);
cmdEncoder->setBuffer(srcBuffer, 0, 1); cmdEncoder->setBuffer(srcBuffer, 0, 1);
cmdEncoder->setBuffer(dstBuffer, dstOffset, 2); cmdEncoder->setBufferForWrite(dstBuffer, dstOffset, 2);
DispatchCompute(contextMtl, cmdEncoder, pipelineState, uniform.indexCount); DispatchCompute(contextMtl, cmdEncoder, pipelineState, uniform.indexCount);
......
...@@ -49,35 +49,36 @@ class Resource : angle::NonCopyable ...@@ -49,35 +49,36 @@ class Resource : angle::NonCopyable
public: public:
virtual ~Resource() {} virtual ~Resource() {}
// Check whether the resource still being used by GPU
bool isBeingUsedByGPU(Context *context) const; 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); void setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing);
const std::atomic<uint64_t> &getCommandBufferQueueSerial() const uint64_t getCommandBufferQueueSerial() const { return mUsageRef->cmdBufferQueueSerial; }
{
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. // resource's content.
bool isCPUReadMemDirty() const { return mUsageRef->cpuReadMemDirty; } bool isCPUReadMemNeedSync() const { return mUsageRef->cpuReadMemNeedSync; }
void resetCPUReadMemDirty() { mUsageRef->cpuReadMemDirty = false; } void resetCPUReadMemNeedSync() { mUsageRef->cpuReadMemNeedSync = false; }
protected: protected:
Resource(); Resource();
// Share the GPU usage ref with other resource // Share the GPU usage ref with other resource
Resource(Resource *other); Resource(Resource *other);
void reset();
private: private:
struct UsageRef struct UsageRef
{ {
// The id of the last command buffer that is using this resource. // 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 // 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. // its content, explicit synchronization call must be invoked.
bool cpuReadMemDirty = false; bool cpuReadMemNeedSync = false;
}; };
// One resource object might just be a view of another resource. For example, a texture 2d // 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) ...@@ -63,29 +63,30 @@ Resource::Resource(Resource *other) : mUsageRef(other->mUsageRef)
ASSERT(mUsageRef); ASSERT(mUsageRef);
} }
void Resource::reset()
{
mUsageRef->cmdBufferQueueSerial = 0;
resetCPUReadMemNeedSync();
}
bool Resource::isBeingUsedByGPU(Context *context) const bool Resource::isBeingUsedByGPU(Context *context) const
{ {
return context->cmdQueue().isResourceBeingUsedByGPU(this); 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); return context->cmdQueue().resourceHasPendingWorks(this);
do }
{
if (curSerial >= serial)
{
return;
}
} while (!mUsageRef->cmdBufferQueueSerial.compare_exchange_weak(
curSerial, serial, std::memory_order_release, std::memory_order_relaxed));
// NOTE(hqle): This is not thread safe, if multiple command buffers on multiple threads void Resource::setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing)
// are writing to it. {
if (writing) if (writing)
{ {
mUsageRef->cpuReadMemDirty = true; mUsageRef->cpuReadMemNeedSync = true;
} }
mUsageRef->cmdBufferQueueSerial = std::max(mUsageRef->cmdBufferQueueSerial, serial);
} }
// Texture implemenetation // Texture implemenetation
...@@ -234,6 +235,8 @@ void Texture::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEnco ...@@ -234,6 +235,8 @@ void Texture::syncContent(ContextMtl *context, mtl::BlitCommandEncoder *blitEnco
if (blitEncoder) if (blitEncoder)
{ {
blitEncoder->synchronizeResource(shared_from_this()); blitEncoder->synchronizeResource(shared_from_this());
this->resetCPUReadMemNeedSync();
} }
#endif #endif
} }
...@@ -244,12 +247,10 @@ void Texture::syncContent(ContextMtl *context) ...@@ -244,12 +247,10 @@ void Texture::syncContent(ContextMtl *context)
// Make sure GPU & CPU contents are synchronized. // Make sure GPU & CPU contents are synchronized.
// NOTE: Only MacOS has separated storage for resource on CPU and GPU and needs explicit // NOTE: Only MacOS has separated storage for resource on CPU and GPU and needs explicit
// synchronization // synchronization
if (this->isCPUReadMemDirty()) if (this->isCPUReadMemNeedSync())
{ {
mtl::BlitCommandEncoder *blitEncoder = context->getBlitCommandEncoder(); mtl::BlitCommandEncoder *blitEncoder = context->getBlitCommandEncoder();
syncContent(context, blitEncoder); syncContent(context, blitEncoder);
this->resetCPUReadMemDirty();
} }
#endif #endif
} }
......
...@@ -315,6 +315,8 @@ struct RenderPassDesc ...@@ -315,6 +315,8 @@ struct RenderPassDesc
RenderPassDepthAttachmentDesc depthAttachment; RenderPassDepthAttachmentDesc depthAttachment;
RenderPassStencilAttachmentDesc stencilAttachment; RenderPassStencilAttachmentDesc stencilAttachment;
void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const;
// This will populate the RenderPipelineOutputDesc with default blend state and // This will populate the RenderPipelineOutputDesc with default blend state and
// MTLColorWriteMaskAll // MTLColorWriteMaskAll
void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const; void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const;
...@@ -333,8 +335,6 @@ struct RenderPassDesc ...@@ -333,8 +335,6 @@ struct RenderPassDesc
uint32_t numColorAttachments = 0; uint32_t numColorAttachments = 0;
}; };
// convert to Metal object
AutoObjCObj<MTLRenderPassDescriptor> ToMetalObj(const RenderPassDesc &desc);
} // namespace mtl } // namespace mtl
} // namespace rx } // namespace rx
......
...@@ -171,7 +171,8 @@ id<MTLTexture> ToObjC(const TextureRef &texture) ...@@ -171,7 +171,8 @@ id<MTLTexture> ToObjC(const TextureRef &texture)
return textureRef ? textureRef->get() : nil; 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, texture);
ANGLE_OBJC_CP_PROPERTY(dst, src, level); ANGLE_OBJC_CP_PROPERTY(dst, src, level);
...@@ -182,40 +183,28 @@ void ToObjC(MTLRenderPassAttachmentDescriptor *dst, const RenderPassAttachmentDe ...@@ -182,40 +183,28 @@ void ToObjC(MTLRenderPassAttachmentDescriptor *dst, const RenderPassAttachmentDe
ANGLE_OBJC_CP_PROPERTY(dst, src, storeActionOptions); ANGLE_OBJC_CP_PROPERTY(dst, src, storeActionOptions);
} }
MTLRenderPassColorAttachmentDescriptor *ToObjC(const RenderPassColorAttachmentDesc &desc) void ToObjC(const RenderPassColorAttachmentDesc &desc,
MTLRenderPassColorAttachmentDescriptor *objCDesc)
{ {
MTLRenderPassColorAttachmentDescriptor *objCDesc = BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
[[MTLRenderPassColorAttachmentDescriptor alloc] init];
ToObjC(objCDesc, desc);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearColor); 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 = BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
[[MTLRenderPassDepthAttachmentDescriptor alloc] init];
ToObjC(objCDesc, desc);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearDepth); 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 = BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
[[MTLRenderPassStencilAttachmentDescriptor alloc] init];
ToObjC(objCDesc, desc);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearStencil); ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, clearStencil);
return [objCDesc ANGLE_MTL_AUTORELEASE];
} }
} // namespace } // namespace
...@@ -756,22 +745,27 @@ bool RenderPassDesc::operator==(const RenderPassDesc &other) const ...@@ -756,22 +745,27 @@ bool RenderPassDesc::operator==(const RenderPassDesc &other) const
} }
// Convert to Metal object // Convert to Metal object
AutoObjCObj<MTLRenderPassDescriptor> ToMetalObj(const RenderPassDesc &desc) void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const
{ {
ANGLE_MTL_OBJC_SCOPE ANGLE_MTL_OBJC_SCOPE
{ {
MTLRenderPassDescriptor *objCDesc = [MTLRenderPassDescriptor renderPassDescriptor]; for (uint32_t i = 0; i < numColorAttachments; ++i)
for (uint32_t i = 0; i < desc.numColorAttachments; ++i)
{ {
[objCDesc.colorAttachments setObject:ToObjC(desc.colorAttachments[i]) ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]);
atIndexedSubscript: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); ToObjC(depthAttachment, objCDesc.depthAttachment);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, stencilAttachment); ToObjC(stencilAttachment, objCDesc.stencilAttachment);
return objCDesc;
} }
} }
......
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