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