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;
}
......
......@@ -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