Files
MapLibreParticleLab/src/mbgl/mtl/texture2d.cpp
2023-11-06 10:12:34 -08:00

242 lines
8.1 KiB
C++

#include <mbgl/mtl/texture2d.hpp>
#include <mbgl/mtl/context.hpp>
#include <mbgl/mtl/render_pass.hpp>
#include <mbgl/mtl/upload_pass.hpp>
#include <Metal/MTLDevice.hpp>
#include <Metal/MTLRenderCommandEncoder.hpp>
#include <Metal/MTLSampler.hpp>
namespace mbgl {
namespace mtl {
Texture2D::Texture2D(Context& context_)
: context(context_) {}
Texture2D::~Texture2D() {
if (metalTexture) {
context.renderingStats().numActiveTextures--;
context.renderingStats().memTextures -= getDataSize();
}
}
gfx::Texture2D& Texture2D::setSamplerConfiguration(const SamplerState& samplerState_) noexcept {
samplerState = samplerState_;
samplerStateDirty = true;
return *this;
}
gfx::Texture2D& Texture2D::setFormat(gfx::TexturePixelType pixelFormat_,
gfx::TextureChannelDataType channelType_) noexcept {
if (pixelFormat_ == pixelFormat && channelType_ == channelType) {
return *this;
}
pixelFormat = pixelFormat_;
channelType = channelType_;
textureDirty = true;
return *this;
}
gfx::Texture2D& Texture2D::setSize(mbgl::Size size_) noexcept {
if (size_ == size) {
return *this;
}
size = size_;
textureDirty = true;
return *this;
}
gfx::Texture2D& Texture2D::setImage(std::shared_ptr<PremultipliedImage> image_) noexcept {
image = std::move(image_);
return *this;
}
size_t Texture2D::getDataSize() const noexcept {
return size.width * size.height * getPixelStride();
}
size_t Texture2D::getPixelStride() const noexcept {
switch (channelType) {
case gfx::TextureChannelDataType::UnsignedByte:
return 1 * numChannels();
case gfx::TextureChannelDataType::HalfFloat:
return 2 * numChannels();
case gfx::TextureChannelDataType::Float:
return 4 * numChannels();
}
}
size_t Texture2D::numChannels() const noexcept {
switch (pixelFormat) {
case gfx::TexturePixelType::RGBA:
return 4;
case gfx::TexturePixelType::Alpha:
return 1;
case gfx::TexturePixelType::Stencil:
return 1;
case gfx::TexturePixelType::Depth:
return 1;
default:
return 0;
}
}
MTL::PixelFormat Texture2D::getMetalPixelFormat() const noexcept {
switch (channelType) {
case gfx::TextureChannelDataType::UnsignedByte:
switch (pixelFormat) {
case gfx::TexturePixelType::RGBA:
return MTL::PixelFormat::PixelFormatRGBA8Unorm;
case gfx::TexturePixelType::Alpha:
return MTL::PixelFormat::PixelFormatA8Unorm;
case gfx::TexturePixelType::Stencil:
return MTL::PixelFormat::PixelFormatStencil8;
default:
assert(false);
return MTL::PixelFormat::PixelFormatInvalid;
}
case gfx::TextureChannelDataType::HalfFloat:
switch (pixelFormat) {
case gfx::TexturePixelType::RGBA:
return MTL::PixelFormat::PixelFormatRGBA16Float;
case gfx::TexturePixelType::Alpha:
return MTL::PixelFormat::PixelFormatR16Float;
default:
assert(false);
return MTL::PixelFormat::PixelFormatInvalid;
}
case gfx::TextureChannelDataType::Float:
switch (pixelFormat) {
case gfx::TexturePixelType::RGBA:
return MTL::PixelFormat::PixelFormatRGBA32Float;
case gfx::TexturePixelType::Alpha:
return MTL::PixelFormat::PixelFormatR32Float;
case gfx::TexturePixelType::Depth:
return MTL::PixelFormat::PixelFormatDepth32Float;
default:
assert(false);
return MTL::PixelFormat::PixelFormatInvalid;
}
default:
assert(false);
return MTL::PixelFormat::PixelFormatInvalid;
}
}
void Texture2D::createMetalTexture() noexcept {
if (size == Size{0, 0}) {
return;
}
metalTexture.reset();
const auto format = getMetalPixelFormat();
if (format == MTL::PixelFormat::PixelFormatInvalid) {
return;
}
// Create a new texture object
if (auto textureDescriptor = NS::RetainPtr(
MTL::TextureDescriptor::texture2DDescriptor(format, size.width, size.height, /*mipmapped=*/false))) {
textureDescriptor->setUsage(usage);
metalTexture = context.createMetalTexture(std::move(textureDescriptor));
}
if (metalTexture) {
textureDirty = false;
context.renderingStats().numCreatedTextures++;
context.renderingStats().numActiveTextures++;
context.renderingStats().memTextures += getDataSize();
}
}
void Texture2D::create() noexcept {
if (textureDirty) {
createMetalTexture();
}
if (samplerStateDirty) {
updateSamplerConfiguration();
}
}
gfx::Texture2D& Texture2D::setUsage(MTL::TextureUsage usage_) noexcept {
usage = usage_;
textureDirty = true;
return *this;
}
MTL::Texture* Texture2D::getMetalTexture() const noexcept {
assert(metalTexture);
return metalTexture.get();
}
void Texture2D::updateSamplerConfiguration() noexcept {
auto samplerDescriptor = NS::RetainPtr(MTL::SamplerDescriptor::alloc()->init());
samplerDescriptor->setMinFilter(samplerState.filter == gfx::TextureFilterType::Nearest
? MTL::SamplerMinMagFilterNearest
: MTL::SamplerMinMagFilterLinear);
samplerDescriptor->setMagFilter(samplerState.filter == gfx::TextureFilterType::Nearest
? MTL::SamplerMinMagFilterNearest
: MTL::SamplerMinMagFilterLinear);
samplerDescriptor->setSAddressMode(samplerState.wrapU == gfx::TextureWrapType::Clamp
? MTL::SamplerAddressModeClampToEdge
: MTL::SamplerAddressModeRepeat);
samplerDescriptor->setTAddressMode(samplerState.wrapV == gfx::TextureWrapType::Clamp
? MTL::SamplerAddressModeClampToEdge
: MTL::SamplerAddressModeRepeat);
metalSamplerState = context.createMetalSamplerState(samplerDescriptor);
}
void Texture2D::bind(const RenderPass& renderPass, int32_t location) noexcept {
assert(!textureDirty);
const auto& encoder = renderPass.getMetalEncoder();
// Update the sampler state if it was changed after resource creation
if (samplerStateDirty) {
updateSamplerConfiguration();
}
encoder->setFragmentTexture(metalTexture.get(), location);
encoder->setFragmentSamplerState(metalSamplerState.get(), location);
context.renderingStats().numTextureBindings++;
}
void Texture2D::unbind(const RenderPass&, int32_t /*location*/) noexcept {
context.renderingStats().numTextureBindings--;
}
void Texture2D::upload(const void* pixelData, const Size& size_) noexcept {
setSize(size_);
if (textureDirty) {
createMetalTexture();
}
if (samplerStateDirty) {
updateSamplerConfiguration();
}
if (pixelData) {
uploadSubRegion(pixelData, size, 0, 0);
}
}
void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16_t xOffset, uint16_t yOffset) noexcept {
assert(metalTexture.get());
assert(!textureDirty);
const MTL::Region region = MTL::Region::Make2D(xOffset, yOffset, size_.width, size_.height);
const NS::UInteger bytesPerRow = size_.width * getPixelStride();
metalTexture->replaceRegion(region, 0, pixelData, bytesPerRow);
context.renderingStats().numTextureUpdates++;
context.renderingStats().textureUpdateBytes += bytesPerRow * size_.height;
}
void Texture2D::upload() noexcept {
if (image && image->valid()) {
setFormat(gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte);
upload(image->data.get(), image->size);
image.reset();
}
}
} // namespace mtl
} // namespace mbgl