Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.39.3/source/MaterialXView/RenderPipelineMetal.mm
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

744 lines
28 KiB
Plaintext

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXView/RenderPipelineMetal.h>
#include <MaterialXView/Viewer.h>
#include <MaterialXRenderMsl/MetalTextureHandler.h>
#include <MaterialXGenMsl/MslShaderGenerator.h>
#include <MaterialXRenderMsl/MetalState.h>
#include <MaterialXRenderMsl/TextureBaker.h>
#include <MaterialXRenderMsl/MetalFramebuffer.h>
#include <MaterialXRenderMsl/MslMaterial.h>
#include <nanogui/messagedialog.h>
#include <MetalPerformanceShaders/MetalPerformanceShaders.h>
namespace
{
const float PI = std::acos(-1.0f);
}
MetalRenderPipeline::MetalRenderPipeline(Viewer* viewerPtr) :
RenderPipeline(viewerPtr)
{
}
void MetalRenderPipeline::initialize(void* metal_device, void* metal_cmd_queue)
{
MTL(initialize((id<MTLDevice>)metal_device,
(id<MTLCommandQueue>)metal_cmd_queue));
}
mx::ImageHandlerPtr MetalRenderPipeline::createImageHandler()
{
return mx::MetalTextureHandler::create(
MTL(device),
mx::StbImageLoader::create());
}
mx::MaterialPtr MetalRenderPipeline::createMaterial()
{
return mx::MslMaterial::create();
}
std::shared_ptr<void> MetalRenderPipeline::createTextureBaker(unsigned int width,
unsigned int height,
mx::Image::BaseType baseType)
{
return std::static_pointer_cast<void>(mx::TextureBakerMsl::create(width, height, baseType));
}
void MetalRenderPipeline::initFramebuffer(int width, int height,
void* color_texture)
{
MTL_PUSH_FRAMEBUFFER(mx::MetalFramebuffer::create(
MTL(device),
width * _viewer->m_pixel_ratio,
height * _viewer->m_pixel_ratio,
4, mx::Image::BaseType::HALF,
MTL(supportsTiledPipeline) ?
(id<MTLTexture>)color_texture : nil,
false, MTLPixelFormatRGBA16Float));
}
void MetalRenderPipeline::resizeFramebuffer(int width, int height,
void* color_texture)
{
MTL_POP_FRAMEBUFFER();
initFramebuffer(width, height, color_texture);
}
void MetalRenderPipeline::updateAlbedoTable(int tableSize)
{
auto& genContext = _viewer->_genContext;
auto& lightHandler = _viewer->_lightHandler;
auto& imageHandler = _viewer->_imageHandler;
if (lightHandler->getAlbedoTable())
{
return;
}
// Create framebuffer.
mx::MetalFramebufferPtr framebuffer = mx::MetalFramebuffer::create(MTL(device),
tableSize, tableSize,
2,
mx::Image::BaseType::FLOAT);
bool captureCommandBuffer = false;
if(captureCommandBuffer)
MTL_TRIGGER_CAPTURE;
MTL_PUSH_FRAMEBUFFER(framebuffer);
MTL(beginCommandBuffer());
MTLRenderPassDescriptor* renderpassDesc = [MTLRenderPassDescriptor new];
[renderpassDesc.colorAttachments[0] setTexture:framebuffer->getColorTexture()];
[renderpassDesc.colorAttachments[0] setClearColor:MTLClearColorMake(0.0f, 0.0f, 0.0f, 0.0f)];
[renderpassDesc.colorAttachments[0] setLoadAction:MTLLoadActionClear];
[renderpassDesc.colorAttachments[0] setStoreAction:MTLStoreActionStore];
[renderpassDesc.depthAttachment setTexture:framebuffer->getDepthTexture()];
[renderpassDesc.depthAttachment setClearDepth:1.0];
[renderpassDesc.depthAttachment setLoadAction:MTLLoadActionClear];
[renderpassDesc.depthAttachment setStoreAction:MTLStoreActionStore];
[renderpassDesc setStencilAttachment:nil];
MTL(beginEncoder(renderpassDesc));
// Create shader.
mx::ShaderPtr hwShader = mx::createAlbedoTableShader(genContext, _viewer->_stdLib, "__ALBEDO_TABLE_SHADER__");
mx::MslMaterialPtr material = mx::MslMaterial::create();
try
{
material->generateShader(hwShader);
}
catch (std::exception& e)
{
new ng::MessageDialog(_viewer, ng::MessageDialog::Type::Warning, "Failed to generate albedo table shader", e.what());
return;
}
// Render albedo table.
material->bindShader();
if (material->getProgram()->hasUniform(mx::HW::ALBEDO_TABLE_SIZE))
{
material->getProgram()->bindUniform(mx::HW::ALBEDO_TABLE_SIZE, mx::Value::createValue(tableSize));
}
material->getProgram()->prepareUsedResources(
MTL(renderCmdEncoder),
_viewer->_identityCamera,
nullptr,
imageHandler,
lightHandler);
_viewer->renderScreenSpaceQuad(material);
MTL(endCommandBuffer());
MTL_POP_FRAMEBUFFER();
if(captureCommandBuffer)
MTL_STOP_CAPTURE;
// Store albedo table image.
imageHandler->releaseRenderResources(lightHandler->getAlbedoTable());
lightHandler->setAlbedoTable(framebuffer->getColorImage(MTL(cmdQueue)));
if (_viewer->_saveGeneratedLights)
{
imageHandler->saveImage("AlbedoTable.exr", lightHandler->getAlbedoTable());
}
}
void MetalRenderPipeline::updatePrefilteredMap()
{
auto& genContext = _viewer->_genContext;
auto& lightHandler = _viewer->_lightHandler;
auto& imageHandler = _viewer->_imageHandler;
if (lightHandler->getEnvPrefilteredMap())
{
return;
}
mx::ImagePtr srcTex = lightHandler->getEnvRadianceMap();
int w = srcTex->getWidth();
int h = srcTex->getHeight();
mx::MetalTextureHandlerPtr mtlImageHandler = std::dynamic_pointer_cast<mx::MetalTextureHandler>(imageHandler);
mx::ImagePtr outTex = mx::Image::create(w, h, 3, mx::Image::BaseType::HALF);
mtlImageHandler->createRenderResources(outTex, true, true);
id<MTLTexture> metalTex = mtlImageHandler->getAssociatedMetalTexture(outTex);
// Create framebuffer.
_prefilterFramebuffer = mx::MetalFramebuffer::create(
MTL(device),
w, h,
4,
mx::Image::BaseType::HALF,
metalTex
);
MTL_PUSH_FRAMEBUFFER(_prefilterFramebuffer);
// Create shader.
mx::ShaderPtr hwShader = mx::createEnvPrefilterShader(genContext, _viewer->_stdLib, "__ENV_PREFILTER__");
mx::MslMaterialPtr material = mx::MslMaterial::create();
try
{
material->generateShader(hwShader);
}
catch (std::exception& e)
{
new ng::MessageDialog(_viewer, ng::MessageDialog::Type::Warning, "Failed to generate convolution shader", e.what());
}
int i = 0;
while (w > 0 && h > 0)
{
MTL(beginCommandBuffer());
MTLRenderPassDescriptor* desc = [MTLRenderPassDescriptor new];
[desc.colorAttachments[0] setTexture:metalTex];
[desc.colorAttachments[0] setLevel:i];
[desc.colorAttachments[0] setLoadAction:MTLLoadActionDontCare];
[desc.colorAttachments[0] setStoreAction:MTLStoreActionStore];
[desc.depthAttachment setTexture:_prefilterFramebuffer->getDepthTexture()];
[desc.depthAttachment setLoadAction:MTLLoadActionDontCare];
[desc.depthAttachment setStoreAction:MTLStoreActionDontCare];
[desc setStencilAttachment:nil];
MTL(beginEncoder(desc));
[MTL(renderCmdEncoder) setDepthStencilState:MTL_DEPTHSTENCIL_STATE(opaque)];
_prefilterFramebuffer->bind(desc);
material->bindShader();
material->getProgram()->bindUniform(mx::HW::ENV_PREFILTER_MIP, mx::Value::createValue(i));
bool prevValue = lightHandler->getUsePrefilteredMap();
lightHandler->setUsePrefilteredMap(false);
material->getProgram()->prepareUsedResources(
MTL(renderCmdEncoder),
_viewer->_identityCamera,
nullptr,
imageHandler,
lightHandler);
lightHandler->setUsePrefilteredMap(prevValue);
_viewer->renderScreenSpaceQuad(material);
MTL(endCommandBuffer());
[desc release];
w /= 2;
h /= 2;
i++;
}
MTL_POP_FRAMEBUFFER();
lightHandler->setEnvPrefilteredMap(outTex);
}
mx::ImagePtr MetalRenderPipeline::getShadowMap(int shadowMapSize)
{
auto& genContext = _viewer->_genContext;
auto& lightHandler = _viewer->_lightHandler;
auto& imageHandler = _viewer->_imageHandler;
auto& shadowCamera = _viewer->_shadowCamera;
auto& stdLib = _viewer->_stdLib;
auto& geometryHandler = _viewer->_geometryHandler;
auto& identityCamera = _viewer->_identityCamera;
mx::MetalTextureHandlerPtr mtlImageHandler =
std::dynamic_pointer_cast<mx::MetalTextureHandler>(imageHandler);
id<MTLTexture> shadowMapTex[SHADOWMAP_TEX_COUNT];
for(int i = 0; i < SHADOWMAP_TEX_COUNT; ++i)
{
if(!_shadowMap[i] || _shadowMap[i]->getWidth() != shadowMapSize ||
!mtlImageHandler->getAssociatedMetalTexture(_shadowMap[i]))
{
_shadowMap[i] = mx::Image::create(shadowMapSize, shadowMapSize, 2, mx::Image::BaseType::FLOAT);
_viewer->_imageHandler->createRenderResources(_shadowMap[i], false, true);
}
shadowMapTex[i] =
mtlImageHandler->getAssociatedMetalTexture(_shadowMap[i]);
}
if (!_viewer->_shadowMap)
{
// Create framebuffer.
if(!_shadowMapFramebuffer)
{
_shadowMapFramebuffer = mx::MetalFramebuffer::create(
MTL(device),
shadowMapSize,
shadowMapSize,
2,
mx::Image::BaseType::FLOAT,
shadowMapTex[0]);
}
MTL_PUSH_FRAMEBUFFER(_shadowMapFramebuffer);
// Generate shaders for shadow rendering.
if (!_viewer->_shadowMaterial)
{
try
{
mx::ShaderPtr hwShader = mx::createDepthShader(genContext, stdLib, "__SHADOW_SHADER__");
_viewer->_shadowMaterial = mx::MslMaterial::create();
_viewer->_shadowMaterial->generateShader(hwShader);
}
catch (std::exception& e)
{
std::cerr << "Failed to generate shadow shader: " << e.what() << std::endl;
_viewer->_shadowMaterial = nullptr;
}
}
if (!_viewer->_shadowBlurMaterial)
{
try
{
mx::ShaderPtr hwShader = mx::createBlurShader(genContext, stdLib, "__SHADOW_BLUR_SHADER__", "gaussian", 1.0f);
_viewer->_shadowBlurMaterial = mx::MslMaterial::create();
_viewer->_shadowBlurMaterial->generateShader(hwShader);
}
catch (std::exception& e)
{
std::cerr << "Failed to generate shadow blur shader: " << e.what() << std::endl;
_viewer->_shadowBlurMaterial = nullptr;
}
}
if (_viewer->_shadowMaterial && _viewer->_shadowBlurMaterial)
{
bool captureShadowGeneration = false;
if(captureShadowGeneration)
MTL_TRIGGER_CAPTURE;
MTL(beginCommandBuffer());
MTLRenderPassDescriptor* renderpassDesc = [MTLRenderPassDescriptor new];
_shadowMapFramebuffer->setColorTexture(shadowMapTex[0]);
_shadowMapFramebuffer->bind(renderpassDesc);
MTL(beginEncoder(renderpassDesc));
[MTL(renderCmdEncoder) setDepthStencilState:MTL_DEPTHSTENCIL_STATE(opaque)];
// Render shadow geometry.
_viewer->_shadowMaterial->bindShader();
for (auto mesh : _viewer->_geometryHandler->getMeshes())
{
_viewer->_shadowMaterial->bindMesh(mesh);
_viewer->_shadowMaterial->bindViewInformation(shadowCamera);
std::static_pointer_cast<mx::MslMaterial>
(_viewer->_shadowMaterial)->prepareUsedResources(
shadowCamera,
geometryHandler,
imageHandler,
lightHandler);
for (size_t i = 0; i < mesh->getPartitionCount(); i++)
{
mx::MeshPartitionPtr geom = mesh->getPartition(i);
_viewer->_shadowMaterial->drawPartition(geom);
}
}
MTL(endCommandBuffer());
// Apply Gaussian blurring.
mx::ImageSamplingProperties blurSamplingProperties;
blurSamplingProperties.uaddressMode = mx::ImageSamplingProperties::AddressMode::CLAMP;
blurSamplingProperties.vaddressMode = mx::ImageSamplingProperties::AddressMode::CLAMP;
blurSamplingProperties.filterType = mx::ImageSamplingProperties::FilterType::CLOSEST;
for (unsigned int i = 0; i < _viewer->_shadowSoftness; i++)
{
MTL(beginCommandBuffer());
_shadowMapFramebuffer->setColorTexture(shadowMapTex[(i+1) % 2]);
_shadowMapFramebuffer->bind(renderpassDesc);
MTL(beginEncoder(renderpassDesc));
_shadowMapFramebuffer->bind(renderpassDesc);
_viewer->_shadowBlurMaterial->bindShader();
std::static_pointer_cast<mx::MslMaterial>
(_viewer->_shadowBlurMaterial)->getProgram()->bindTexture(
_viewer->_imageHandler,
"image_file_tex",
_shadowMap[i % 2],
blurSamplingProperties);
std::static_pointer_cast<mx::MslMaterial>
(_viewer->_shadowBlurMaterial)->prepareUsedResources(
identityCamera,
geometryHandler,
imageHandler,
lightHandler);
_viewer->_shadowBlurMaterial->unbindGeometry();
_viewer->renderScreenSpaceQuad(_viewer->_shadowBlurMaterial);
MTL(endCommandBuffer());
}
MTL_POP_FRAMEBUFFER();
if(captureShadowGeneration)
MTL_STOP_CAPTURE;
[renderpassDesc release];
}
// Reset frame timing after shadow generation.
_viewer->resetFrameTiming();
}
_viewer->_shadowMap = _shadowMap[_viewer->_shadowSoftness % 2];
return _viewer->_shadowMap;
}
void MetalRenderPipeline::renderFrame(void* color_texture, int shadowMapSize, const char* dirLightNodeCat)
{
auto& genContext = _viewer->_genContext;
auto& lightHandler = _viewer->_lightHandler;
auto& imageHandler = _viewer->_imageHandler;
auto& viewCamera = _viewer->_viewCamera;
auto& envCamera = _viewer->_envCamera;
auto& shadowCamera = _viewer->_shadowCamera;
float lightRotation = _viewer->_lightRotation;
auto& searchPath = _viewer->_searchPath;
auto& geometryHandler = _viewer->_geometryHandler;
// Update prefiltered environment.
if (lightHandler->getUsePrefilteredMap() && !_viewer->_materialAssignments.empty())
{
updatePrefilteredMap();
}
// Update lighting state.
lightHandler->setLightTransform(mx::Matrix44::createRotationY(lightRotation / 180.0f * M_PI));
// Update shadow state.
mx::ShadowState shadowState;
shadowState.ambientOcclusionGain = _viewer->_ambientOcclusionGain;
mx::NodePtr dirLight = lightHandler->getFirstLightOfCategory(dirLightNodeCat);
if (genContext.getOptions().hwShadowMap && dirLight)
{
mx::ImagePtr shadowMap = getShadowMap(shadowMapSize);
if (shadowMap)
{
shadowState.shadowMap = shadowMap;
shadowState.shadowMatrix = viewCamera->getWorldMatrix().getInverse() *
shadowCamera->getWorldViewProjMatrix();
}
else
{
genContext.getOptions().hwShadowMap = false;
}
}
bool captureFrame = false;
if(captureFrame)
MTL_TRIGGER_CAPTURE;
bool useTiledPipeline;
if(@available(macOS 11.0, ios 14.0, *))
{
useTiledPipeline = MTL(supportsTiledPipeline);
}
else
{
useTiledPipeline = false;
}
MTL(beginCommandBuffer());
MTLRenderPassDescriptor* renderpassDesc = [MTLRenderPassDescriptor new];
if(useTiledPipeline)
{
[renderpassDesc.colorAttachments[0] setTexture:(id<MTLTexture>)color_texture];
}
else
{
[renderpassDesc.colorAttachments[0] setTexture:MTL(currentFramebuffer())->getColorTexture()];
}
[renderpassDesc.colorAttachments[0] setClearColor:MTLClearColorMake(
_viewer->m_background[0],
_viewer->m_background[1],
_viewer->m_background[2],
_viewer->m_background[3])];
[renderpassDesc.colorAttachments[0] setLoadAction:MTLLoadActionClear];
[renderpassDesc.colorAttachments[0] setStoreAction:MTLStoreActionStore];
[renderpassDesc.depthAttachment setTexture:MTL(currentFramebuffer())->getDepthTexture()];
[renderpassDesc.depthAttachment setClearDepth:1.0];
[renderpassDesc.depthAttachment setLoadAction:MTLLoadActionClear];
[renderpassDesc.depthAttachment setStoreAction:MTLStoreActionStore];
[renderpassDesc setStencilAttachment:nil];
MTL(beginEncoder(renderpassDesc));
[MTL(renderCmdEncoder) setFrontFacingWinding:MTLWindingClockwise];
// Environment background
if (_viewer->_drawEnvironment)
{
[MTL(renderCmdEncoder) setDepthStencilState:MTL_DEPTHSTENCIL_STATE(envMap)];
mx::MslMaterialPtr envMaterial = std::static_pointer_cast<mx::MslMaterial>(_viewer->getEnvironmentMaterial());
if (envMaterial)
{
const mx::MeshList& meshes = _viewer->_envGeometryHandler->getMeshes();
mx::MeshPartitionPtr envPart = !meshes.empty() ? meshes[0]->getPartition(0) : nullptr;
if (envPart)
{
// Apply rotation to the environment shader.
float longitudeOffset = (lightRotation / 360.0f) + 0.5f;
envMaterial->modifyUniform("longitude/in2", mx::Value::createValue(longitudeOffset));
// Apply light intensity to the environment shader.
envMaterial->modifyUniform("envImageAdjusted/in2", mx::Value::createValue(lightHandler->getEnvLightIntensity()));
// Render the environment mesh.
[MTL(renderCmdEncoder) setCullMode:MTLCullModeNone];
envMaterial->bindShader();
envMaterial->bindMesh(meshes[0]);
envMaterial->bindViewInformation(envCamera);
envMaterial->bindImages(imageHandler, searchPath, false);
envMaterial->prepareUsedResources(envCamera,
_viewer->_envGeometryHandler,
imageHandler,
lightHandler);
envMaterial->drawPartition(envPart);
[MTL(renderCmdEncoder) setCullMode:MTLCullModeNone];
}
}
else
{
_viewer->_drawEnvironment = false;
}
}
// Enable backface culling if requested.
if (!_viewer->_renderDoubleSided)
{
[MTL(renderCmdEncoder) setCullMode:MTLCullModeBack];
}
// Opaque pass
[MTL(renderCmdEncoder) setDepthStencilState:MTL_DEPTHSTENCIL_STATE(opaque)];
for (const auto& assignment : _viewer->_materialAssignments)
{
mx::MeshPartitionPtr geom = assignment.first;
mx::MslMaterialPtr material = std::static_pointer_cast<mx::MslMaterial>(assignment.second);
shadowState.ambientOcclusionMap = _viewer->getAmbientOcclusionImage(material);
if (!material)
{
continue;
}
material->bindShader();
material->bindMesh(_viewer->_geometryHandler->findParentMesh(geom));
if (material->getProgram()->hasUniform(mx::HW::ALPHA_THRESHOLD))
{
material->getProgram()->bindUniform(mx::HW::ALPHA_THRESHOLD, mx::Value::createValue(0.99f));
}
material->bindViewInformation(viewCamera);
material->bindLighting(lightHandler, imageHandler, shadowState);
material->bindImages(imageHandler, _viewer->_searchPath);
material->prepareUsedResources(viewCamera,
geometryHandler,
imageHandler,
lightHandler);
material->drawPartition(geom);
material->unbindImages(imageHandler);
}
// Transparent pass
if (_viewer->_renderTransparency)
{
[MTL(renderCmdEncoder) setDepthStencilState:MTL_DEPTHSTENCIL_STATE(transparent)];
for (const auto& assignment : _viewer->_materialAssignments)
{
mx::MeshPartitionPtr geom = assignment.first;
mx::MslMaterialPtr material = std::static_pointer_cast<mx::MslMaterial>(assignment.second);
shadowState.ambientOcclusionMap = _viewer->getAmbientOcclusionImage(material);
if (!material || !material->hasTransparency())
{
continue;
}
material->bindShader();
material->bindMesh(geometryHandler->findParentMesh(geom));
if (material->getProgram()->hasUniform(mx::HW::ALPHA_THRESHOLD))
{
material->getProgram()->bindUniform(mx::HW::ALPHA_THRESHOLD, mx::Value::createValue(0.001f));
}
material->bindViewInformation(viewCamera);
material->bindLighting(lightHandler, imageHandler, shadowState);
material->bindImages(imageHandler, searchPath);
material->prepareUsedResources(viewCamera,
geometryHandler,
imageHandler,
lightHandler);
material->drawPartition(geom);
material->unbindImages(imageHandler);
}
}
if (!_viewer->_renderDoubleSided)
{
[MTL(renderCmdEncoder) setCullMode:MTLCullModeNone];
}
// Wireframe pass
if (_viewer->_outlineSelection)
{
mx::MslMaterialPtr wireMaterial =
std::static_pointer_cast<mx::MslMaterial>(_viewer->getWireframeMaterial());
if (wireMaterial)
{
[MTL(renderCmdEncoder) setCullMode:MTLCullModeNone];
[MTL(renderCmdEncoder) setTriangleFillMode:MTLTriangleFillModeLines];
wireMaterial->bindShader();
wireMaterial->bindMesh(geometryHandler->findParentMesh(_viewer->getSelectedGeometry()));
wireMaterial->bindViewInformation(viewCamera);
wireMaterial->prepareUsedResources(viewCamera,
geometryHandler,
imageHandler,
lightHandler);
wireMaterial->drawPartition(_viewer->getSelectedGeometry());
[MTL(renderCmdEncoder) setTriangleFillMode:MTLTriangleFillModeFill];
[MTL(renderCmdEncoder) setCullMode:MTLCullModeNone];
}
else
{
_viewer->_outlineSelection = false;
}
}
#ifdef MAC_OS_VERSION_11_0
if(useTiledPipeline)
{
if(@available(macOS 11.0, ios 14.0, *))
{
[MTL(renderCmdEncoder) setRenderPipelineState:MTL(linearToSRGB_pso)];
[MTL(renderCmdEncoder) dispatchThreadsPerTile:MTLSizeMake(
MTL(renderCmdEncoder).tileWidth,
MTL(renderCmdEncoder).tileHeight, 1)];
}
}
if(!useTiledPipeline)
#endif
{
MTL(endEncoder());
[renderpassDesc.colorAttachments[0] setTexture:(id<MTLTexture>)color_texture];
MTL(beginEncoder(renderpassDesc));
[MTL(renderCmdEncoder) setRenderPipelineState:MTL(linearToSRGB_pso)];
[MTL(renderCmdEncoder)
setFragmentTexture:MTL(currentFramebuffer())->getColorTexture()
atIndex:0];
[MTL(renderCmdEncoder) drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
}
MTL(endCommandBuffer());
if(captureFrame)
MTL_STOP_CAPTURE;
[renderpassDesc release];
}
void MetalRenderPipeline::bakeTextures()
{
auto& imageHandler = _viewer->_imageHandler;
mx::MaterialPtr material = _viewer->getSelectedMaterial();
mx::DocumentPtr doc = material ? material->getDocument() : nullptr;
if (!doc)
{
return;
}
{
// Construct a texture baker.
mx::Image::BaseType baseType = _viewer->_bakeHdr ? mx::Image::BaseType::FLOAT : mx::Image::BaseType::UINT8;
mx::UnsignedIntPair bakingRes = _viewer->computeBakingResolution(doc);
mx::TextureBakerPtr baker = std::static_pointer_cast<mx::TextureBakerPtr::element_type>(createTextureBaker(bakingRes.first, bakingRes.second, baseType));
baker->setupUnitSystem(_viewer->_stdLib);
baker->setDistanceUnit(_viewer->_genContext.getOptions().targetDistanceUnit);
baker->setAverageImages(_viewer->_bakeAverage);
baker->setOptimizeConstants(_viewer->_bakeOptimize);
baker->writeDocumentPerMaterial(_viewer->_bakeDocumentPerMaterial);
// Assign our existing image handler, releasing any existing render resources for cached images.
imageHandler->releaseRenderResources();
baker->setImageHandler(imageHandler);
// Extend the image search path to include material source folders.
mx::FileSearchPath extendedSearchPath = _viewer->_searchPath;
extendedSearchPath.append(_viewer->_materialSearchPath);
// Bake all materials in the active document.
try
{
baker->bakeAllMaterials(doc, extendedSearchPath, _viewer->_bakeFilename);
}
catch (std::exception& e)
{
std::cerr << "Error in texture baking: " << e.what() << std::endl;
}
// Release any render resources generated by the baking process.
imageHandler->releaseRenderResources();
}
}
mx::ImagePtr MetalRenderPipeline::getFrameImage()
{
unsigned int width = MTL(currentFramebuffer())->getWidth();
unsigned int height = MTL(currentFramebuffer())->getHeight();
MTL(waitForCompletion());
id<MTLTexture> srcTexture = MTL(supportsTiledPipeline) ?
(id<MTLTexture>)_viewer->_colorTexture :
MTL(currentFramebuffer())->getColorTexture();
mx::MetalFramebufferPtr framebuffer = mx::MetalFramebuffer::create(
MTL(device),
width, height, 4,
mx::Image::BaseType::UINT8,
nil,
false, MTLPixelFormatBGRA8Unorm);
id<MTLTexture> dstTexture = framebuffer->getColorTexture();
id<MTLCommandQueue> cmdQueue = MTL(cmdQueue);
// Copy with format conversion
MPSImageConversion* conversion = [[MPSImageConversion alloc] initWithDevice:MTL(device)];
id<MTLCommandBuffer> cmdBuffer = [cmdQueue commandBuffer];
[conversion encodeToCommandBuffer:cmdBuffer sourceTexture:srcTexture destinationTexture:dstTexture];
[cmdBuffer commit];
[cmdBuffer waitUntilCompleted];
mx::ImagePtr frame = framebuffer->getColorImage(cmdQueue);
// Flips the captured image
std::vector<unsigned char> tmp(frame->getRowStride());
unsigned int half_height = height / 2;
unsigned char* resourceBuffer = static_cast<unsigned char*>(frame->getResourceBuffer());
for(unsigned int i = 0; i < half_height; ++i)
{
memcpy(tmp.data(),
&resourceBuffer[i*frame->getRowStride()], frame->getRowStride());
memcpy(&resourceBuffer[i*frame->getRowStride()],
&resourceBuffer[(height - i - 1) * frame->getRowStride()], frame->getRowStride());
memcpy(&resourceBuffer[(height - i - 1) * frame->getRowStride()],
tmp.data(), frame->getRowStride());
}
framebuffer = nullptr;
return frame;
}