Files
MapLibreParticleLab/src/mbgl/mtl/render_pass.cpp
2026-01-07 14:21:34 +02:00

240 lines
7.8 KiB
C++

#include <mbgl/mtl/render_pass.hpp>
#include <mbgl/mtl/command_encoder.hpp>
#include <mbgl/mtl/renderable_resource.hpp>
#include <mbgl/mtl/context.hpp>
#include <mbgl/util/logging.hpp>
#include <Metal/Metal.hpp>
namespace mbgl {
namespace mtl {
RenderPass::RenderPass(CommandEncoder& commandEncoder_, const char* name, const gfx::RenderPassDescriptor& descriptor)
: descriptor(descriptor),
commandEncoder(commandEncoder_) {
auto& resource = descriptor.renderable.getResource<RenderableResource>();
resource.bind();
if (const auto& buffer = resource.getCommandBuffer()) {
if (auto rpd = resource.getRenderPassDescriptor()) {
if (descriptor.clearColor) {
if (auto copy = NS::TransferPtr(rpd->copy())) {
if (auto* colorTarget = copy->colorAttachments()->object(0)) {
const auto& c = *descriptor.clearColor;
colorTarget->setLoadAction(MTL::LoadActionClear);
colorTarget->setClearColor(MTL::ClearColor::Make(c.r, c.g, c.b, c.a));
rpd = std::move(copy);
}
}
}
encoder = NS::RetainPtr(buffer->renderCommandEncoder(rpd.get()));
const auto& texture = rpd->colorAttachments()->object(0)->texture();
width = texture->width();
height = texture->height();
}
}
assert(encoder);
// Push the groups already accumulated by the encoder
commandEncoder.visitDebugGroups([this](const auto& group) {
debugGroups.emplace_back(gfx::DebugGroup<gfx::RenderPass>{*this, group.c_str()});
});
// Push the group for the name provided
debugGroups.emplace_back(gfx::DebugGroup<gfx::RenderPass>{*this, name});
// Let the encoder pass along any groups pushed to it after this
commandEncoder.trackRenderPass(this);
commandEncoder.context.performCleanup();
}
RenderPass::~RenderPass() {
commandEncoder.forgetRenderPass(this);
endEncoding();
}
void RenderPass::endEncoding() {
debugGroups.clear();
if (encoder) {
encoder->endEncoding();
encoder.reset();
}
resetState();
}
void RenderPass::resetState() {
currentPipelineState.reset();
currentDepthStencilState.reset();
currentStencilReferenceValue = 0;
for (int i = 0; i < maxBinds; ++i) {
vertexBinds[i].reset();
fragmentBinds[i].reset();
fragmentTextureBindings[i].reset();
fragmentSamplerStates[i].reset();
}
currentCullMode = MTL::CullModeNone;
currentWinding = MTL::WindingClockwise;
currentScissorRect = {.x = 0, .y = 0, .width = 0, .height = 0};
}
namespace {
constexpr auto missing = "<none>";
NS::String* toNSString(const char* str) {
return NS::String::string(str ? str : missing, NS::UTF8StringEncoding);
}
} // namespace
void RenderPass::pushDebugGroup(const char* name) {
assert(encoder);
if (encoder) {
encoder->pushDebugGroup(toNSString(name));
}
}
void RenderPass::popDebugGroup() {
assert(encoder);
if (encoder) {
encoder->popDebugGroup();
}
}
void RenderPass::addDebugSignpost(const char* name) {
assert(encoder);
if (encoder) {
encoder->insertDebugSignpost(toNSString(name));
}
}
void RenderPass::bindVertex(const BufferResource& buf, std::size_t offset, std::size_t index, std::size_t size) {
const auto actualSize = size ? size : buf.getSizeInBytes() - offset;
assert(actualSize <= buf.getSizeInBytes());
assert(0 <= index && index < maxBinds);
if (0 <= index && index < maxBinds) {
if (auto& bind = vertexBinds[index]) {
// Is this the same buffer already bound to this index?
if (bind->buf == &buf && !buf.needReBind(bind->version)) {
// Yes, but is the offset different?
if (bind->offset != offset) {
// Yes, update just the offset
buf.updateVertexBindOffset(encoder, offset, index, actualSize);
bind->offset = offset;
}
return;
}
}
vertexBinds[index] = BindInfo{.buf = &buf, .size = actualSize, .offset = offset};
}
buf.bindVertex(encoder, offset, index, actualSize);
}
void RenderPass::unbindVertex(std::size_t index) {
vertexBinds[index] = std::nullopt;
}
void RenderPass::bindFragment(const BufferResource& buf, std::size_t offset, std::size_t index, std::size_t size) {
const auto actualSize = size ? size : buf.getSizeInBytes() - offset;
assert(actualSize <= buf.getSizeInBytes());
assert(0 <= index && index < maxBinds);
if (0 <= index && index < maxBinds) {
if (auto& bind = fragmentBinds[index]) {
// Is this the same buffer already bound to this index?
if (bind->buf == &buf && !buf.needReBind(bind->version)) {
// Yes, but is the offset different?
if (bind->offset != offset) {
// Yes, update just the offset
buf.updateFragmentBindOffset(encoder, offset, index, actualSize);
bind->offset = offset;
}
return;
}
}
fragmentBinds[index] = BindInfo{.buf = &buf, .size = actualSize, .offset = offset};
}
buf.bindFragment(encoder, offset, index, actualSize);
}
void RenderPass::unbindFragment(std::size_t index) {
fragmentBinds[index] = std::nullopt;
}
void RenderPass::setDepthStencilState(const MTLDepthStencilStatePtr& state) {
if (state != currentDepthStencilState) {
currentDepthStencilState = state;
encoder->setDepthStencilState(currentDepthStencilState.get());
}
}
void RenderPass::setStencilReference(int32_t referenceValue) {
if (referenceValue != currentStencilReferenceValue) {
currentStencilReferenceValue = referenceValue;
encoder->setStencilReferenceValue(currentStencilReferenceValue);
}
}
void RenderPass::setFragmentTexture(const MTLTexturePtr& texture, int32_t location) {
assert(0 <= location && location < maxBinds);
if (0 <= location && location < maxBinds) {
if (fragmentTextureBindings[location] != texture) {
fragmentTextureBindings[location] = texture;
encoder->setFragmentTexture(texture.get(), location);
}
}
}
void RenderPass::setFragmentSamplerState(const MTLSamplerStatePtr& state, int32_t location) {
assert(0 <= location && location < maxBinds);
if (0 <= location && location < maxBinds) {
if (fragmentSamplerStates[location] != state) {
fragmentSamplerStates[location] = state;
encoder->setFragmentSamplerState(state.get(), location);
}
}
}
/// Set the render pipeline state
void RenderPass::setRenderPipelineState(const MTLRenderPipelineStatePtr& pipelineState) {
if (pipelineState != currentPipelineState) {
currentPipelineState = pipelineState;
encoder->setRenderPipelineState(currentPipelineState.get());
}
}
void RenderPass::setCullMode(const MTL::CullMode mode) {
if (mode != currentCullMode) {
encoder->setCullMode(mode);
currentCullMode = mode;
}
}
void RenderPass::setFrontFacingWinding(const MTL::Winding winding) {
if (winding != currentWinding) {
encoder->setFrontFacingWinding(winding);
currentWinding = winding;
}
}
void RenderPass::setScissorRect(MTL::ScissorRect rect) {
if (rect.x != currentScissorRect.x || rect.y != currentScissorRect.y || rect.width != currentScissorRect.width ||
rect.height != currentScissorRect.height) {
if (rect.width + rect.x > width) {
rect.width = width - rect.x;
}
if (rect.height + rect.y > height) {
rect.height = height - rect.y;
}
encoder->setScissorRect(rect);
currentScissorRect = rect;
}
}
} // namespace mtl
} // namespace mbgl