axmol/cocos/renderer/backend/metal/CommandBufferMTL.mm

392 lines
13 KiB
Plaintext

#include "CommandBufferMTL.h"
#include "BufferMTL.h"
#include "DeviceMTL.h"
#include "RenderPipelineMTL.h"
#include "TextureMTL.h"
#include "Utils.h"
#include "../Macros.h"
#include "BufferManager.h"
CC_BACKEND_BEGIN
namespace
{
MTLPrimitiveType toMTLPrimitive(PrimitiveType primitiveType)
{
MTLPrimitiveType ret = MTLPrimitiveTypeTriangle;
switch (primitiveType)
{
case PrimitiveType::POINT:
ret = MTLPrimitiveTypePoint;
break;
case PrimitiveType::LINE:
ret = MTLPrimitiveTypeLine;
break;
case PrimitiveType::LINE_STRIP:
ret = MTLPrimitiveTypeLineStrip;
break;
case PrimitiveType::TRIANGLE:
ret = MTLPrimitiveTypeTriangle;
break;
case PrimitiveType::TRIANGLE_STRIP:
ret = MTLPrimitiveTypeTriangleStrip;
break;
default:
break;
}
return ret;
}
MTLIndexType toMTLIndexType(IndexFormat indexFormat)
{
if (IndexFormat::U_SHORT == indexFormat)
return MTLIndexTypeUInt16;
else
return MTLIndexTypeUInt32;
}
MTLCullMode toMTLCullMode(CullMode mode)
{
switch (mode) {
case CullMode::NONE:
return MTLCullModeNone;
case CullMode::FRONT:
return MTLCullModeFront;
case CullMode::BACK:
return MTLCullModeBack;
}
}
MTLRenderPassDescriptor* toMTLRenderPassDescriptor(const RenderPassDescriptor& descriptor)
{
MTLRenderPassDescriptor* mtlDescritpor = [MTLRenderPassDescriptor renderPassDescriptor];
// Set color attachments.
if (descriptor.needColorAttachment)
{
bool hasCustomColorAttachment = false;
for (int i = 0; i < MAX_COLOR_ATTCHMENT; ++i)
{
if (! descriptor.colorAttachmentsTexture[i])
continue;
mtlDescritpor.colorAttachments[i].texture = static_cast<TextureMTL*>(descriptor.colorAttachmentsTexture[i])->getMTLTexture();
if (descriptor.needClearColor)
{
mtlDescritpor.colorAttachments[i].loadAction = MTLLoadActionClear;
mtlDescritpor.colorAttachments[i].clearColor = MTLClearColorMake(descriptor.clearColorValue[0],
descriptor.clearColorValue[1],
descriptor.clearColorValue[2],
descriptor.clearColorValue[3]);
}
else
mtlDescritpor.colorAttachments[i].loadAction = MTLLoadActionLoad;
hasCustomColorAttachment = true;
}
if (!hasCustomColorAttachment)
{
mtlDescritpor.colorAttachments[0].texture = DeviceMTL::getCurrentDrawable().texture;
if (descriptor.needClearColor)
{
mtlDescritpor.colorAttachments[0].loadAction = MTLLoadActionClear;
mtlDescritpor.colorAttachments[0].clearColor = MTLClearColorMake(descriptor.clearColorValue[0],
descriptor.clearColorValue[1],
descriptor.clearColorValue[2],
descriptor.clearColorValue[3]);
}
else
mtlDescritpor.colorAttachments[0].loadAction = MTLLoadActionLoad;
}
mtlDescritpor.colorAttachments[0].storeAction = MTLStoreActionStore;
}
// Set depth/stencil attachment
if (descriptor.needDepthAttachment)
{
if (descriptor.depthAttachmentTexture)
mtlDescritpor.depthAttachment.texture = static_cast<TextureMTL*>(descriptor.depthAttachmentTexture)->getMTLTexture();
else
mtlDescritpor.depthAttachment.texture = Utils::getDefaultDepthStencilTexture();
if (descriptor.needClearDepth)
{
mtlDescritpor.depthAttachment.loadAction = MTLLoadActionClear;
mtlDescritpor.depthAttachment.clearDepth = descriptor.clearDepthValue;
}
else
mtlDescritpor.depthAttachment.loadAction = MTLLoadActionLoad;
mtlDescritpor.depthAttachment.storeAction = MTLStoreActionStore;
}
if (descriptor.needStencilAttachment)
{
if (descriptor.stencilAttachmentTexture)
mtlDescritpor.stencilAttachment.texture = static_cast<TextureMTL*>(descriptor.stencilAttachmentTexture)->getMTLTexture();
else
mtlDescritpor.stencilAttachment.texture = Utils::getDefaultDepthStencilTexture();
if (descriptor.needClearStencil)
{
mtlDescritpor.stencilAttachment.loadAction = MTLLoadActionClear;
mtlDescritpor.stencilAttachment.clearStencil = descriptor.clearStencilValue;
}
else
mtlDescritpor.stencilAttachment.loadAction = MTLLoadActionLoad;
mtlDescritpor.stencilAttachment.storeAction = MTLStoreActionStore;
}
return mtlDescritpor;
}
}
CommandBufferMTL::CommandBufferMTL(DeviceMTL* deviceMTL)
: _deviceMTL(deviceMTL)
, _mtlCommandQueue(deviceMTL->getMTLCommandQueue())
, _frameBoundarySemaphore(dispatch_semaphore_create(MAX_INFLIGHT_BUFFER))
{
}
CommandBufferMTL::~CommandBufferMTL()
{
dispatch_semaphore_signal(_frameBoundarySemaphore);
}
void CommandBufferMTL::beginFrame()
{
dispatch_semaphore_wait(_frameBoundarySemaphore, DISPATCH_TIME_FOREVER);
_mtlCommandBuffer = [_mtlCommandQueue commandBuffer];
[_mtlCommandBuffer retain];
BufferManager::beginFrame();
}
void CommandBufferMTL::beginRenderPass(const RenderPassDescriptor& descriptor)
{
auto mtlDescriptor = toMTLRenderPassDescriptor(descriptor);
_renderTargetHeight = (unsigned int)mtlDescriptor.colorAttachments[0].texture.height;
_mtlRenderEncoder = [_mtlCommandBuffer renderCommandEncoderWithDescriptor:mtlDescriptor];
[_mtlRenderEncoder retain];
// [_mtlRenderEncoder setFrontFacingWinding:MTLWindingCounterClockwise];
}
void CommandBufferMTL::setRenderPipeline(RenderPipeline* renderPipeline)
{
CC_SAFE_RETAIN(renderPipeline);
CC_SAFE_RELEASE(_renderPipelineMTL);
_renderPipelineMTL = static_cast<RenderPipelineMTL*>(renderPipeline);
[_mtlRenderEncoder setRenderPipelineState:_renderPipelineMTL->getMTLRenderPipelineState()];
}
void CommandBufferMTL::setViewport(int x, int y, unsigned int w, unsigned int h)
{
MTLViewport viewport;
viewport.originX = x;
viewport.originY = (int)(_renderTargetHeight - y - h);
viewport.width = w;
viewport.height = h;
viewport.znear = -1;
viewport.zfar = 1;
[_mtlRenderEncoder setViewport:viewport];
}
void CommandBufferMTL::setCullMode(CullMode mode)
{
[_mtlRenderEncoder setCullMode:toMTLCullMode(mode)];
}
void CommandBufferMTL::setVertexBuffer(unsigned int index, Buffer* buffer)
{
// Vertex buffer is bound in index 0.
[_mtlRenderEncoder setVertexBuffer:static_cast<BufferMTL*>(buffer)->getMTLBuffer()
offset:0
atIndex:0];
}
void CommandBufferMTL::setProgramState(ProgramState* programState)
{
CC_SAFE_RETAIN(programState);
CC_SAFE_RELEASE(_programState);
_programState = programState;
}
void CommandBufferMTL::setIndexBuffer(Buffer* buffer)
{
assert(buffer != nullptr);
if (!buffer)
return;
_mtlIndexBuffer = static_cast<BufferMTL*>(buffer)->getMTLBuffer();
[_mtlIndexBuffer retain];
}
void CommandBufferMTL::drawArrays(PrimitiveType primitiveType, unsigned int start, unsigned int count)
{
prepareDrawing();
[_mtlRenderEncoder drawPrimitives:toMTLPrimitive(primitiveType)
vertexStart:start
vertexCount:count];
}
void CommandBufferMTL::drawElements(PrimitiveType primitiveType, IndexFormat indexType, unsigned int count, unsigned int offset)
{
prepareDrawing();
[_mtlRenderEncoder drawIndexedPrimitives:toMTLPrimitive(primitiveType)
indexCount:count
indexType:toMTLIndexType(indexType)
indexBuffer:_mtlIndexBuffer
indexBufferOffset:offset];
}
void CommandBufferMTL::endRenderPass()
{
afterDraw();
[_mtlRenderEncoder endEncoding];
[_mtlRenderEncoder release];
}
void CommandBufferMTL::endFrame()
{
[_mtlCommandBuffer presentDrawable:DeviceMTL::getCurrentDrawable()];
[_mtlCommandBuffer addCompletedHandler:^(id<MTLCommandBuffer> commandBuffer) {
// GPU work is complete
// Signal the semaphore to start the CPU work
dispatch_semaphore_signal(_frameBoundarySemaphore);
}];
[_mtlCommandBuffer commit];
[_mtlCommandBuffer release];
DeviceMTL::resetCurrentDrawable();
}
void CommandBufferMTL::afterDraw()
{
if (_mtlIndexBuffer)
{
[_mtlIndexBuffer release];
_mtlIndexBuffer = nullptr;
}
CC_SAFE_RELEASE_NULL(_programState);
}
void CommandBufferMTL::prepareDrawing() const
{
setUniformBuffer();
setTextures();
auto mtlDepthStencilState = _renderPipelineMTL->getMTLDepthStencilState();
if (mtlDepthStencilState)
{
[_mtlRenderEncoder setDepthStencilState:mtlDepthStencilState];
[_mtlRenderEncoder setStencilFrontReferenceValue:_stencilReferenceValueFront
backReferenceValue:_stencilReferenceValueBack];
}
}
void CommandBufferMTL::setTextures() const
{
if (_programState)
{
doSetTextures(true);
doSetTextures(false);
}
}
void CommandBufferMTL::doSetTextures(bool isVertex) const
{
const auto& bindTextureInfos = (isVertex) ? _programState->getVertexTextureInfos() : _programState->getFragmentTextureInfos();
for(const auto& iter : bindTextureInfos)
{
//FIXME: should support texture array.
int i = 0;
auto location = iter.first;
const auto& textures = iter.second.textures;
const auto& mtlTexture = static_cast<TextureMTL*>(textures[i]);
if (isVertex)
{
[_mtlRenderEncoder setVertexTexture:mtlTexture->getMTLTexture()
atIndex:location];
[_mtlRenderEncoder setVertexSamplerState:mtlTexture->getMTLSamplerState()
atIndex:location];
}
else
{
[_mtlRenderEncoder setFragmentTexture:mtlTexture->getMTLTexture()
atIndex:location];
[_mtlRenderEncoder setFragmentSamplerState:mtlTexture->getMTLSamplerState()
atIndex:location];
}
++i;
}
}
void CommandBufferMTL::setUniformBuffer() const
{
if (_programState)
{
// Uniform buffer is bound to index 1.
const auto& vertexUniformBuffer = _renderPipelineMTL->getVertexUniformBuffer();
const auto& vertexUniformInfo = _programState->getVertexUniformInfos();
if (vertexUniformBuffer)
{
uint32_t size = fillUniformBuffer(vertexUniformBuffer.get(), vertexUniformInfo);
[_mtlRenderEncoder setVertexBytes:vertexUniformBuffer.get()
length:size atIndex:1];
}
const auto& fragUniformBuffer = _renderPipelineMTL->getFragmentUniformBuffer();
const auto& fragUniformInfo = _programState->getFragmentUniformInfos();
if (fragUniformBuffer)
{
uint32_t size = fillUniformBuffer(fragUniformBuffer.get(), fragUniformInfo);
[_mtlRenderEncoder setFragmentBytes:fragUniformBuffer.get()
length:size
atIndex:1];
}
}
}
unsigned int CommandBufferMTL::fillUniformBuffer(uint8_t* buffer, const std::vector<UniformBuffer>& unifornInfo) const
{
uint32_t offset = 0;
for(const auto& iter : unifornInfo)
{
const auto& bindUniformInfo = iter.uniformInfo;
memcpy(buffer + bindUniformInfo.location, iter.data, bindUniformInfo.bufferSize);
offset += bindUniformInfo.bufferSize;
}
return offset;
}
void CommandBufferMTL::setLineWidth(float lineWidth)
{
}
void CommandBufferMTL::setScissorRect(bool isEnabled, float x, float y, float width, float height)
{
if(!isEnabled)
return;
MTLScissorRect scissorRect;
scissorRect.x = x;
scissorRect.y = _renderTargetHeight - height - y;
scissorRect.width = width;
scissorRect.height = height;
[_mtlRenderEncoder setScissorRect:scissorRect];
}
CC_BACKEND_END