2022-07-27 11:25:25 -04:00
|
|
|
#include "common.hpp"
|
|
|
|
|
|
|
|
|
|
#include "../internal.hpp"
|
2025-04-04 01:59:30 -06:00
|
|
|
#include "../webgpu/gpu.hpp"
|
|
|
|
|
#include "aurora/aurora.h"
|
2022-07-27 11:25:25 -04:00
|
|
|
#include "texture.hpp"
|
|
|
|
|
#include "texture_convert.hpp"
|
2026-04-01 00:49:28 -06:00
|
|
|
#include "../gx/gx_fmt.hpp"
|
2022-07-27 11:25:25 -04:00
|
|
|
|
2025-04-04 01:59:30 -06:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include <fmt/format.h>
|
2026-04-12 15:35:28 -06:00
|
|
|
#include <tracy/Tracy.hpp>
|
2025-04-04 01:59:30 -06:00
|
|
|
#include <webgpu/webgpu_cpp.h>
|
2022-07-27 11:25:25 -04:00
|
|
|
|
|
|
|
|
namespace aurora::gfx {
|
|
|
|
|
using webgpu::g_device;
|
|
|
|
|
using webgpu::g_queue;
|
|
|
|
|
|
2025-04-04 01:59:30 -06:00
|
|
|
namespace {
|
|
|
|
|
Module Log("aurora::gfx");
|
|
|
|
|
|
2026-04-20 20:33:14 -06:00
|
|
|
wgpu::Extent3D physical_size(wgpu::Extent3D size, TextureFormatInfo info) {
|
|
|
|
|
const uint32_t width = ((size.width + info.blockWidth - 1) / info.blockWidth) * info.blockWidth;
|
|
|
|
|
const uint32_t height = ((size.height + info.blockHeight - 1) / info.blockHeight) * info.blockHeight;
|
|
|
|
|
return {.width = width, .height = height, .depthOrArrayLayers = size.depthOrArrayLayers};
|
|
|
|
|
}
|
2026-05-30 09:57:21 -06:00
|
|
|
|
|
|
|
|
bool setup_swizzle(wgpu::TextureComponentSwizzleDescriptor& swizzle, u32 format) noexcept {
|
|
|
|
|
if (!webgpu::g_textureComponentSwizzleSupported) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (format) {
|
|
|
|
|
case GX_TF_R8_PC:
|
|
|
|
|
swizzle.swizzle.r = wgpu::ComponentSwizzle::R;
|
|
|
|
|
swizzle.swizzle.g = wgpu::ComponentSwizzle::R;
|
|
|
|
|
swizzle.swizzle.b = wgpu::ComponentSwizzle::R;
|
|
|
|
|
swizzle.swizzle.a = wgpu::ComponentSwizzle::R;
|
|
|
|
|
return true;
|
|
|
|
|
case GX_TF_RG8_PC:
|
|
|
|
|
swizzle.swizzle.r = wgpu::ComponentSwizzle::R;
|
|
|
|
|
swizzle.swizzle.g = wgpu::ComponentSwizzle::R;
|
|
|
|
|
swizzle.swizzle.b = wgpu::ComponentSwizzle::R;
|
|
|
|
|
swizzle.swizzle.a = wgpu::ComponentSwizzle::G;
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-20 20:33:14 -06:00
|
|
|
} // namespace
|
|
|
|
|
|
2022-08-02 16:37:56 -04:00
|
|
|
TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 format, ArrayRef<uint8_t> data,
|
2025-04-07 20:12:16 -06:00
|
|
|
bool tlut, const char* label) noexcept {
|
2026-04-12 15:35:28 -06:00
|
|
|
ZoneScoped;
|
|
|
|
|
|
2022-07-27 11:25:25 -04:00
|
|
|
auto handle = new_dynamic_texture_2d(width, height, mips, format, label);
|
2026-04-20 20:33:14 -06:00
|
|
|
auto& ref = *handle;
|
2022-07-27 11:25:25 -04:00
|
|
|
|
2026-04-20 20:33:14 -06:00
|
|
|
ConvertedTexture converted;
|
2022-07-27 11:25:25 -04:00
|
|
|
if (ref.gxFormat != InvalidTextureFormat) {
|
2025-04-07 20:12:16 -06:00
|
|
|
if (tlut) {
|
|
|
|
|
CHECK(ref.size.height == 1, "new_static_texture_2d[{}]: expected tlut height 1, got {}", label, ref.size.height);
|
|
|
|
|
CHECK(ref.mipCount == 1, "new_static_texture_2d[{}]: expected tlut mipCount 1, got {}", label, ref.mipCount);
|
2026-04-20 20:33:14 -06:00
|
|
|
converted = convert_tlut(ref.gxFormat, ref.size.width, data);
|
2025-04-07 20:12:16 -06:00
|
|
|
} else {
|
2026-04-20 20:33:14 -06:00
|
|
|
converted = convert_texture(ref.gxFormat, ref.size.width, ref.size.height, ref.mipCount, data);
|
2025-04-07 20:12:16 -06:00
|
|
|
}
|
2026-04-20 20:33:14 -06:00
|
|
|
if (!converted.data.empty()) {
|
|
|
|
|
data = converted.data;
|
|
|
|
|
ref.hasArbitraryMips = converted.hasArbitraryMips;
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t offset = 0;
|
|
|
|
|
for (uint32_t mip = 0; mip < mips; ++mip) {
|
2022-08-02 16:37:56 -04:00
|
|
|
const wgpu::Extent3D mipSize{
|
2022-07-27 11:25:25 -04:00
|
|
|
.width = std::max(ref.size.width >> mip, 1u),
|
|
|
|
|
.height = std::max(ref.size.height >> mip, 1u),
|
|
|
|
|
.depthOrArrayLayers = ref.size.depthOrArrayLayers,
|
|
|
|
|
};
|
|
|
|
|
const auto info = format_info(ref.format);
|
|
|
|
|
const auto physicalSize = physical_size(mipSize, info);
|
|
|
|
|
const uint32_t widthBlocks = physicalSize.width / info.blockWidth;
|
|
|
|
|
const uint32_t heightBlocks = physicalSize.height / info.blockHeight;
|
|
|
|
|
const uint32_t bytesPerRow = widthBlocks * info.blockSize;
|
|
|
|
|
const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers;
|
2022-08-09 02:05:33 -04:00
|
|
|
CHECK(offset + dataSize <= data.size(), "new_static_texture_2d[{}]: expected at least {} bytes, got {}", label,
|
|
|
|
|
offset + dataSize, data.size());
|
2025-04-03 00:12:22 -06:00
|
|
|
const wgpu::TexelCopyTextureInfo dstView{
|
2022-07-27 11:25:25 -04:00
|
|
|
.texture = ref.texture,
|
|
|
|
|
.mipLevel = mip,
|
|
|
|
|
};
|
2026-04-10 18:18:29 -06:00
|
|
|
if constexpr (UseTextureBuffer) {
|
2026-06-04 23:43:13 -06:00
|
|
|
queue_texture_upload_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks, std::move(dstView),
|
|
|
|
|
physicalSize);
|
2026-04-10 18:18:29 -06:00
|
|
|
} else {
|
|
|
|
|
const wgpu::TexelCopyBufferLayout dataLayout{
|
|
|
|
|
.bytesPerRow = bytesPerRow,
|
|
|
|
|
.rowsPerImage = heightBlocks,
|
|
|
|
|
};
|
|
|
|
|
g_queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize);
|
|
|
|
|
}
|
2022-07-27 11:25:25 -04:00
|
|
|
offset += dataSize;
|
|
|
|
|
}
|
|
|
|
|
if (data.size() != UINT32_MAX && offset < data.size()) {
|
2025-04-06 16:37:05 -06:00
|
|
|
Log.warn("new_static_texture_2d[{}]: texture used {} bytes, but given {} bytes", label, offset, data.size());
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 00:49:28 -06:00
|
|
|
TextureHandle new_dynamic_texture_2d(uint32_t width, uint32_t height, uint32_t mips, u32 gxFormat,
|
2022-07-27 11:25:25 -04:00
|
|
|
const char* label) noexcept {
|
2026-04-09 03:19:58 +02:00
|
|
|
ZoneScopedS(3);
|
2026-04-01 00:49:28 -06:00
|
|
|
const auto wgpuFormat = to_wgpu(gxFormat);
|
2022-08-02 16:37:56 -04:00
|
|
|
const wgpu::Extent3D size{
|
2022-07-27 11:25:25 -04:00
|
|
|
.width = width,
|
|
|
|
|
.height = height,
|
|
|
|
|
.depthOrArrayLayers = 1,
|
|
|
|
|
};
|
2022-08-02 16:37:56 -04:00
|
|
|
const wgpu::TextureDescriptor textureDescriptor{
|
2022-07-27 11:25:25 -04:00
|
|
|
.label = label,
|
2022-08-02 16:37:56 -04:00
|
|
|
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst,
|
|
|
|
|
.dimension = wgpu::TextureDimension::e2D,
|
2022-07-27 11:25:25 -04:00
|
|
|
.size = size,
|
|
|
|
|
.format = wgpuFormat,
|
|
|
|
|
.mipLevelCount = mips,
|
|
|
|
|
.sampleCount = 1,
|
|
|
|
|
};
|
2026-04-01 00:49:28 -06:00
|
|
|
auto texture = g_device.CreateTexture(&textureDescriptor);
|
2025-04-03 21:03:08 -06:00
|
|
|
const auto viewLabel = fmt::format("{} view", label);
|
2026-03-11 02:19:34 -06:00
|
|
|
wgpu::TextureViewDescriptor textureViewDescriptor{
|
2022-07-27 11:25:25 -04:00
|
|
|
.label = viewLabel.c_str(),
|
|
|
|
|
.format = wgpuFormat,
|
2022-08-02 16:37:56 -04:00
|
|
|
.dimension = wgpu::TextureViewDimension::e2D,
|
2022-07-27 11:25:25 -04:00
|
|
|
.mipLevelCount = mips,
|
|
|
|
|
};
|
2026-05-30 09:57:21 -06:00
|
|
|
wgpu::TextureComponentSwizzleDescriptor swizzle;
|
|
|
|
|
if (setup_swizzle(swizzle, gxFormat)) {
|
|
|
|
|
textureViewDescriptor.nextInChain = &swizzle;
|
|
|
|
|
}
|
2022-08-02 16:37:56 -04:00
|
|
|
auto textureView = texture.CreateView(&textureViewDescriptor);
|
2026-04-01 00:49:28 -06:00
|
|
|
return std::make_shared<TextureRef>(std::move(texture), std::move(textureView), wgpu::TextureView{}, size, wgpuFormat,
|
2026-04-20 20:33:14 -06:00
|
|
|
mips, gxFormat);
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
|
2026-04-01 00:49:28 -06:00
|
|
|
TextureHandle new_render_texture(uint32_t width, uint32_t height, u32 gxFormat, const char* label) noexcept {
|
2026-04-12 15:35:28 -06:00
|
|
|
ZoneScoped;
|
|
|
|
|
|
2025-04-03 00:12:22 -06:00
|
|
|
const auto wgpuFormat = webgpu::g_graphicsConfig.surfaceConfiguration.format;
|
2022-08-02 16:37:56 -04:00
|
|
|
const wgpu::Extent3D size{
|
2022-07-27 11:25:25 -04:00
|
|
|
.width = width,
|
|
|
|
|
.height = height,
|
|
|
|
|
.depthOrArrayLayers = 1,
|
|
|
|
|
};
|
2022-08-02 16:37:56 -04:00
|
|
|
const wgpu::TextureDescriptor textureDescriptor{
|
2022-07-27 11:25:25 -04:00
|
|
|
.label = label,
|
2026-04-01 00:49:28 -06:00
|
|
|
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment,
|
2022-08-02 16:37:56 -04:00
|
|
|
.dimension = wgpu::TextureDimension::e2D,
|
2022-07-27 11:25:25 -04:00
|
|
|
.size = size,
|
|
|
|
|
.format = wgpuFormat,
|
|
|
|
|
.mipLevelCount = 1,
|
|
|
|
|
.sampleCount = 1,
|
|
|
|
|
};
|
2026-04-01 00:49:28 -06:00
|
|
|
auto texture = g_device.CreateTexture(&textureDescriptor);
|
|
|
|
|
|
|
|
|
|
// Create texture view for color attachments
|
2025-04-03 21:03:08 -06:00
|
|
|
const auto viewLabel = fmt::format("{} view", label);
|
2026-04-01 00:49:28 -06:00
|
|
|
wgpu::TextureViewDescriptor textureViewDescriptor{
|
2022-07-27 11:25:25 -04:00
|
|
|
.label = viewLabel.c_str(),
|
|
|
|
|
.format = wgpuFormat,
|
2022-08-02 16:37:56 -04:00
|
|
|
.dimension = wgpu::TextureViewDimension::e2D,
|
2022-07-27 11:25:25 -04:00
|
|
|
};
|
2026-04-01 00:49:28 -06:00
|
|
|
auto attachmentTextureView = texture.CreateView(&textureViewDescriptor);
|
2026-04-20 20:33:14 -06:00
|
|
|
wgpu::TextureView sampleTextureView = attachmentTextureView;
|
2026-04-01 00:49:28 -06:00
|
|
|
return std::make_shared<TextureRef>(std::move(texture), std::move(sampleTextureView),
|
2026-04-20 20:33:14 -06:00
|
|
|
std::move(attachmentTextureView), size, wgpuFormat, 1, gxFormat);
|
2026-04-01 00:49:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextureHandle new_conv_texture(uint32_t width, uint32_t height, u32 gxFormat, const char* label) noexcept {
|
2026-04-12 15:35:28 -06:00
|
|
|
ZoneScoped;
|
|
|
|
|
|
2026-04-01 00:49:28 -06:00
|
|
|
const auto wgpuFormat = to_wgpu(gxFormat);
|
|
|
|
|
const wgpu::Extent3D size{
|
|
|
|
|
.width = width,
|
|
|
|
|
.height = height,
|
|
|
|
|
.depthOrArrayLayers = 1,
|
|
|
|
|
};
|
|
|
|
|
const wgpu::TextureDescriptor textureDescriptor{
|
|
|
|
|
.label = label,
|
|
|
|
|
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
|
|
|
|
|
.dimension = wgpu::TextureDimension::e2D,
|
|
|
|
|
.size = size,
|
|
|
|
|
.format = wgpuFormat,
|
|
|
|
|
.mipLevelCount = 1,
|
|
|
|
|
.sampleCount = 1,
|
|
|
|
|
};
|
2022-08-02 16:37:56 -04:00
|
|
|
auto texture = g_device.CreateTexture(&textureDescriptor);
|
2026-04-01 00:49:28 -06:00
|
|
|
|
|
|
|
|
// Create texture view for color attachments
|
|
|
|
|
const auto viewLabel = fmt::format("{} view", label);
|
|
|
|
|
wgpu::TextureViewDescriptor textureViewDescriptor{
|
|
|
|
|
.label = viewLabel.c_str(),
|
|
|
|
|
.format = wgpuFormat,
|
|
|
|
|
.dimension = wgpu::TextureViewDimension::e2D,
|
|
|
|
|
};
|
|
|
|
|
auto attachmentTextureView = texture.CreateView(&textureViewDescriptor);
|
2026-04-20 20:33:14 -06:00
|
|
|
wgpu::TextureView sampleTextureView = attachmentTextureView;
|
2026-04-01 00:49:28 -06:00
|
|
|
return std::make_shared<TextureRef>(std::move(texture), std::move(sampleTextureView),
|
2026-04-20 20:33:14 -06:00
|
|
|
std::move(attachmentTextureView), size, wgpuFormat, 1, gxFormat);
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 20:33:14 -06:00
|
|
|
void write_texture(TextureRef& ref, ArrayRef<uint8_t> data) noexcept {
|
2026-04-12 15:35:28 -06:00
|
|
|
ZoneScoped;
|
|
|
|
|
|
2026-04-20 20:33:14 -06:00
|
|
|
ConvertedTexture converted;
|
2022-07-27 11:25:25 -04:00
|
|
|
if (ref.gxFormat != InvalidTextureFormat) {
|
2026-04-20 20:33:14 -06:00
|
|
|
converted = convert_texture(ref.gxFormat, ref.size.width, ref.size.height, ref.mipCount, data);
|
|
|
|
|
ref.hasArbitraryMips = converted.hasArbitraryMips;
|
|
|
|
|
if (!converted.data.empty()) {
|
|
|
|
|
data = converted.data;
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t offset = 0;
|
|
|
|
|
for (uint32_t mip = 0; mip < ref.mipCount; ++mip) {
|
2022-08-02 16:37:56 -04:00
|
|
|
const wgpu::Extent3D mipSize{
|
2022-07-27 11:25:25 -04:00
|
|
|
.width = std::max(ref.size.width >> mip, 1u),
|
|
|
|
|
.height = std::max(ref.size.height >> mip, 1u),
|
|
|
|
|
.depthOrArrayLayers = ref.size.depthOrArrayLayers,
|
|
|
|
|
};
|
|
|
|
|
const auto info = format_info(ref.format);
|
|
|
|
|
const auto physicalSize = physical_size(mipSize, info);
|
|
|
|
|
const uint32_t widthBlocks = physicalSize.width / info.blockWidth;
|
|
|
|
|
const uint32_t heightBlocks = physicalSize.height / info.blockHeight;
|
|
|
|
|
const uint32_t bytesPerRow = widthBlocks * info.blockSize;
|
|
|
|
|
const uint32_t dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers;
|
2022-08-09 02:05:33 -04:00
|
|
|
CHECK(offset + dataSize <= data.size(), "write_texture: expected at least {} bytes, got {}", offset + dataSize,
|
|
|
|
|
data.size());
|
2025-04-03 00:12:22 -06:00
|
|
|
const wgpu::TexelCopyTextureInfo dstView{
|
2022-07-27 11:25:25 -04:00
|
|
|
.texture = ref.texture,
|
|
|
|
|
.mipLevel = mip,
|
|
|
|
|
};
|
2026-04-10 18:18:29 -06:00
|
|
|
if constexpr (UseTextureBuffer) {
|
2026-06-04 23:43:13 -06:00
|
|
|
queue_texture_upload_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks, std::move(dstView),
|
|
|
|
|
physicalSize);
|
2026-04-10 18:18:29 -06:00
|
|
|
} else {
|
|
|
|
|
const wgpu::TexelCopyBufferLayout dataLayout{
|
|
|
|
|
.bytesPerRow = bytesPerRow,
|
|
|
|
|
.rowsPerImage = heightBlocks,
|
|
|
|
|
};
|
|
|
|
|
g_queue.WriteTexture(&dstView, data.data() + offset, dataSize, &dataLayout, &physicalSize);
|
|
|
|
|
}
|
2022-07-27 11:25:25 -04:00
|
|
|
offset += dataSize;
|
|
|
|
|
}
|
|
|
|
|
if (data.size() != UINT32_MAX && offset < data.size()) {
|
2025-04-06 16:37:05 -06:00
|
|
|
Log.warn("write_texture: texture used {} bytes, but given {} bytes", offset, data.size());
|
2022-07-27 11:25:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} // namespace aurora::gfx
|