mirror of
https://github.com/encounter/aurora.git
synced 2026-07-09 18:19:33 -07:00
Introduce render worker & revamp RmlUi backend
This commit is contained in:
@@ -3,6 +3,7 @@ add_library(aurora_gx STATIC
|
||||
lib/gfx/common.cpp
|
||||
lib/gfx/depth_peek.cpp
|
||||
lib/gfx/pipeline_cache.cpp
|
||||
lib/gfx/render_worker.cpp
|
||||
lib/gfx/dds_io.cpp
|
||||
lib/gfx/tex_copy_conv.cpp
|
||||
lib/gfx/tex_palette_conv.cpp
|
||||
@@ -43,3 +44,10 @@ set_target_properties(aurora_gx PROPERTIES FOLDER "aurora")
|
||||
target_link_libraries(aurora_gx PUBLIC aurora::core dawn::webgpu_dawn xxhash)
|
||||
target_link_libraries(aurora_gx PRIVATE absl::btree absl::flat_hash_map sqlite3 TracyClient PNG::PNG)
|
||||
target_compile_definitions(aurora_gx PRIVATE WEBGPU_DAWN)
|
||||
|
||||
if (AURORA_ENABLE_RMLUI)
|
||||
target_sources(aurora_gx PRIVATE
|
||||
lib/rmlui/pipeline.cpp
|
||||
lib/rmlui/pipeline.hpp
|
||||
)
|
||||
endif ()
|
||||
|
||||
+89
-76
@@ -176,16 +176,11 @@ AuroraInfo initialize(int argc, char* argv[], const AuroraConfig& config) noexce
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
wgpu::TextureView g_currentView;
|
||||
#endif
|
||||
|
||||
void shutdown() noexcept {
|
||||
#ifdef AURORA_ENABLE_RMLUI
|
||||
rmlui::shutdown();
|
||||
#endif
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
g_currentView = {};
|
||||
imgui::shutdown();
|
||||
gfx::shutdown();
|
||||
webgpu::shutdown();
|
||||
@@ -221,36 +216,10 @@ bool begin_frame() noexcept {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
wgpu::SurfaceTexture surfaceTexture;
|
||||
g_surface.GetCurrentTexture(&surfaceTexture);
|
||||
switch (surfaceTexture.status) {
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal:
|
||||
g_currentView = surfaceTexture.texture.CreateView();
|
||||
break;
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Timeout:
|
||||
Log.warn("Surface texture acquisition timed out");
|
||||
return false;
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::SuccessSuboptimal:
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Outdated:
|
||||
Log.info("Surface texture is {}, reconfiguring swapchain", magic_enum::enum_name(surfaceTexture.status));
|
||||
webgpu::refresh_surface(false);
|
||||
return false;
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Lost:
|
||||
Log.warn("Surface texture is {}, releasing surface", magic_enum::enum_name(surfaceTexture.status));
|
||||
webgpu::release_surface();
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Error:
|
||||
Log.warn("Surface texture is {}, dropping surface", magic_enum::enum_name(surfaceTexture.status));
|
||||
g_surface = {};
|
||||
return false;
|
||||
default:
|
||||
Log.error("Failed to get surface texture: {}", magic_enum::enum_name(surfaceTexture.status));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
imgui::new_frame(window::get_window_size());
|
||||
if (!gfx::begin_frame()) {
|
||||
g_currentView = {};
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
@@ -261,33 +230,51 @@ void end_frame() noexcept {
|
||||
ZoneScoped;
|
||||
#ifdef AURORA_ENABLE_GX
|
||||
gx::fifo::drain();
|
||||
const auto encoderDescriptor = wgpu::CommandEncoderDescriptor{
|
||||
.label = "Redraw encoder",
|
||||
};
|
||||
auto encoder = g_device.CreateCommandEncoder(&encoderDescriptor);
|
||||
gfx::end_frame(encoder);
|
||||
gfx::render(encoder);
|
||||
{
|
||||
gfx::finish();
|
||||
auto imguiDrawData = imgui::freeze();
|
||||
|
||||
const auto& presentSource = webgpu::present_source();
|
||||
const auto viewport = webgpu::calculate_present_viewport(webgpu::g_graphicsConfig.surfaceConfiguration.width,
|
||||
webgpu::g_graphicsConfig.surfaceConfiguration.height,
|
||||
presentSource.size.width, presentSource.size.height);
|
||||
|
||||
wgpu::BindGroup rmlBindGroup;
|
||||
#if AURORA_ENABLE_RMLUI
|
||||
if (rmlui::is_initialized()) {
|
||||
rmlBindGroup = rmlui::record_frame(viewport);
|
||||
}
|
||||
#endif
|
||||
|
||||
gfx::end_frame([rmlBindGroup = std::move(rmlBindGroup), viewport,
|
||||
imguiDrawData = std::move(imguiDrawData)](wgpu::CommandEncoder& encoder) {
|
||||
window::SurfaceLock surfaceLock;
|
||||
if (window::is_presentable() && g_surface && g_currentView) {
|
||||
const auto& presentSource = webgpu::present_source();
|
||||
auto viewport = webgpu::calculate_present_viewport(webgpu::g_graphicsConfig.surfaceConfiguration.width,
|
||||
webgpu::g_graphicsConfig.surfaceConfiguration.height,
|
||||
presentSource.size.width, presentSource.size.height);
|
||||
const auto& resampledSource = webgpu::resample_present_source(encoder, viewport);
|
||||
wgpu::BindGroup presentBindGroup = webgpu::create_copy_bind_group(resampledSource);
|
||||
#if AURORA_ENABLE_RMLUI
|
||||
if (rmlui::is_initialized()) {
|
||||
const auto rmlOutput = rmlui::render(encoder, viewport, resampledSource);
|
||||
if (rmlOutput.texture != nullptr) {
|
||||
presentBindGroup = rmlOutput.copyBindGroup;
|
||||
}
|
||||
wgpu::Texture currentTexture;
|
||||
wgpu::TextureView currentView;
|
||||
auto surfaceStatus = wgpu::SurfaceGetCurrentTextureStatus::Error;
|
||||
if (window::is_presentable() && g_surface) {
|
||||
ZoneScopedN("Acquire texture");
|
||||
wgpu::SurfaceTexture surfaceTexture;
|
||||
g_surface.GetCurrentTexture(&surfaceTexture);
|
||||
surfaceStatus = surfaceTexture.status;
|
||||
if (surfaceStatus == wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
|
||||
currentTexture = std::move(surfaceTexture.texture);
|
||||
currentView = currentTexture.CreateView();
|
||||
}
|
||||
}
|
||||
|
||||
const bool canPresent = currentTexture && currentView;
|
||||
if (canPresent) {
|
||||
wgpu::BindGroup presentBindGroup;
|
||||
if (rmlBindGroup) {
|
||||
presentBindGroup = rmlBindGroup;
|
||||
} else {
|
||||
const auto& resampledSource = webgpu::resample_present_source(encoder, viewport);
|
||||
presentBindGroup = webgpu::create_copy_bind_group(resampledSource);
|
||||
}
|
||||
#endif
|
||||
{
|
||||
const std::array attachments{
|
||||
wgpu::RenderPassColorAttachment{
|
||||
.view = g_currentView,
|
||||
.view = currentView,
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
},
|
||||
@@ -309,7 +296,7 @@ void end_frame() noexcept {
|
||||
{
|
||||
const std::array attachments{
|
||||
wgpu::RenderPassColorAttachment{
|
||||
.view = g_currentView,
|
||||
.view = currentView,
|
||||
.loadOp = wgpu::LoadOp::Load,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
},
|
||||
@@ -322,44 +309,70 @@ void end_frame() noexcept {
|
||||
const auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
|
||||
pass.SetViewport(0.f, 0.f, static_cast<float>(webgpu::g_graphicsConfig.surfaceConfiguration.width),
|
||||
static_cast<float>(webgpu::g_graphicsConfig.surfaceConfiguration.height), 0.f, 1.f);
|
||||
imgui::render(pass);
|
||||
imgui::render(pass, imguiDrawData);
|
||||
pass.End();
|
||||
}
|
||||
} else {
|
||||
Log.info("Skipping present; window not presentable");
|
||||
webgpu::release_surface();
|
||||
}
|
||||
const wgpu::CommandBufferDescriptor cmdBufDescriptor{.label = "Redraw command buffer"};
|
||||
const auto buffer = encoder.Finish(&cmdBufDescriptor);
|
||||
g_queue.Submit(1, &buffer);
|
||||
gfx::after_submit();
|
||||
if (window::is_presentable() && g_surface) {
|
||||
{
|
||||
ZoneScopedN("Queue Submit");
|
||||
g_queue.Submit(1, &buffer);
|
||||
}
|
||||
if (canPresent && g_surface) {
|
||||
ZoneScopedN("Present");
|
||||
auto presentStatus = g_surface.Present();
|
||||
if (presentStatus != wgpu::Status::Success) {
|
||||
Log.warn("Surface present failed: {}", static_cast<int>(presentStatus));
|
||||
webgpu::release_surface();
|
||||
}
|
||||
} else if (g_surface) {
|
||||
webgpu::release_surface();
|
||||
switch (surfaceStatus) {
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Timeout:
|
||||
Log.warn("Surface texture acquisition timed out");
|
||||
break;
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::SuccessSuboptimal:
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Outdated:
|
||||
Log.info("Surface texture is {}, reconfiguring swapchain", magic_enum::enum_name(surfaceStatus));
|
||||
webgpu::refresh_surface(false);
|
||||
break;
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Lost:
|
||||
Log.warn("Surface texture is {}, releasing surface", magic_enum::enum_name(surfaceStatus));
|
||||
webgpu::release_surface();
|
||||
break;
|
||||
case wgpu::SurfaceGetCurrentTextureStatus::Error:
|
||||
Log.warn("Surface texture is {}, dropping surface", magic_enum::enum_name(surfaceStatus));
|
||||
g_surface = {};
|
||||
break;
|
||||
default:
|
||||
if (!window::is_presentable()) {
|
||||
webgpu::release_surface();
|
||||
} else {
|
||||
Log.error("Failed to get surface texture: {}", magic_enum::enum_name(surfaceStatus));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_currentView = {};
|
||||
}
|
||||
gfx::after_submit();
|
||||
|
||||
TracyPlotConfig("aurora: lastVertSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastUniformSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastIndexSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastStorageSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastTextureUploadSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastVertSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastUniformSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastIndexSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastStorageSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
TracyPlotConfig("aurora: lastTextureUploadSize", tracy::PlotFormatType::Memory, false, true, 0);
|
||||
|
||||
TracyPlot("aurora: queuedPipelines", static_cast<int64_t>(gfx::g_stats.queuedPipelines));
|
||||
TracyPlot("aurora: createdPipelines", static_cast<int64_t>(gfx::g_stats.createdPipelines));
|
||||
TracyPlot("aurora: drawCallCount", static_cast<int64_t>(gfx::g_stats.drawCallCount));
|
||||
TracyPlot("aurora: mergedDrawCallCount", static_cast<int64_t>(gfx::g_stats.mergedDrawCallCount));
|
||||
TracyPlot("aurora: lastVertSize", static_cast<int64_t>(gfx::g_stats.lastVertSize));
|
||||
TracyPlot("aurora: lastUniformSize", static_cast<int64_t>(gfx::g_stats.lastUniformSize));
|
||||
TracyPlot("aurora: lastIndexSize", static_cast<int64_t>(gfx::g_stats.lastIndexSize));
|
||||
TracyPlot("aurora: lastStorageSize", static_cast<int64_t>(gfx::g_stats.lastStorageSize));
|
||||
TracyPlot("aurora: lastTextureUploadSize", static_cast<int64_t>(gfx::g_stats.lastTextureUploadSize));
|
||||
TracyPlot("aurora: queuedPipelines", static_cast<int64_t>(gfx::g_stats.queuedPipelines));
|
||||
TracyPlot("aurora: createdPipelines", static_cast<int64_t>(gfx::g_stats.createdPipelines));
|
||||
TracyPlot("aurora: drawCallCount", static_cast<int64_t>(gfx::g_stats.drawCallCount));
|
||||
TracyPlot("aurora: mergedDrawCallCount", static_cast<int64_t>(gfx::g_stats.mergedDrawCallCount));
|
||||
TracyPlot("aurora: lastVertSize", static_cast<int64_t>(gfx::g_stats.lastVertSize));
|
||||
TracyPlot("aurora: lastUniformSize", static_cast<int64_t>(gfx::g_stats.lastUniformSize));
|
||||
TracyPlot("aurora: lastIndexSize", static_cast<int64_t>(gfx::g_stats.lastIndexSize));
|
||||
TracyPlot("aurora: lastStorageSize", static_cast<int64_t>(gfx::g_stats.lastStorageSize));
|
||||
TracyPlot("aurora: lastTextureUploadSize", static_cast<int64_t>(gfx::g_stats.lastTextureUploadSize));
|
||||
});
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ void shutdown_rumble() noexcept {
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
#elif !defined(SDL_PLATFORM_IOS)
|
||||
#elif !defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
|
||||
bool rumble_available() noexcept { return false; }
|
||||
|
||||
void rumble(const uint16_t lowFreq, const uint16_t highFreq, const uint16_t durationMs) noexcept {
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <dolphin/gx/GXCpu2Efb.h>
|
||||
|
||||
void GXPeekZ(u16 x, u16 y, u32* z) {
|
||||
aurora::gfx::depth_peek::poll();
|
||||
|
||||
if (z != nullptr) {
|
||||
u32 value = 0;
|
||||
if (aurora::gfx::depth_peek::read_latest(x, y, value)) {
|
||||
|
||||
@@ -58,13 +58,12 @@ void GXCallDisplayList(const void* data, u32 nbytes) {
|
||||
__GXSendFlushPrim();
|
||||
}
|
||||
|
||||
// Drain the internal FIFO so that any pending CP register writes
|
||||
// (VCD, VAT, etc.) are processed into g_gxState before the display
|
||||
// list's draw commands reference them.
|
||||
aurora::gx::fifo::drain();
|
||||
// Write display list contents to the FIFO
|
||||
aurora::gx::fifo::write_data(data, nbytes);
|
||||
|
||||
// Process the display list through the command processor
|
||||
aurora::gx::fifo::process(static_cast<const u8*>(data), nbytes, true);
|
||||
// TEMP: debugging aid
|
||||
// aurora::gx::fifo::drain();
|
||||
// aurora::gx::fifo::process(static_cast<const u8*>(data), nbytes, true);
|
||||
}
|
||||
|
||||
void GXCallDisplayListLE(const void* data, u32 nbytes) {
|
||||
|
||||
@@ -148,6 +148,8 @@ void GXSetDispCopyGamma(GXGamma gamma) {}
|
||||
void GXCopyDisp(void* dest, GXBool clear) {}
|
||||
|
||||
void GXCopyTex(void* dest, GXBool clear) {
|
||||
aurora::gx::fifo::drain();
|
||||
|
||||
const auto rect = aurora::gx::map_logical_scissor(g_gxState.texCopySrc);
|
||||
const auto [dstWidth, dstHeight] = scale_copy_dst(g_gxState.texCopyDstWidth, g_gxState.texCopyDstHeight);
|
||||
const auto texCopyFmt = g_gxState.texCopyFmt;
|
||||
|
||||
@@ -48,9 +48,10 @@ void GXEnd() {
|
||||
}
|
||||
sInBegin = false;
|
||||
}
|
||||
if (!aurora::gx::fifo::in_display_list()) {
|
||||
aurora::gx::fifo::drain();
|
||||
}
|
||||
// TEMP: debugging aid
|
||||
// if (!aurora::gx::fifo::in_display_list()) {
|
||||
// aurora::gx::fifo::drain();
|
||||
// }
|
||||
}
|
||||
|
||||
void GXPosition3f32(f32 x, f32 y, f32 z) {
|
||||
|
||||
+767
-264
File diff suppressed because it is too large
Load Diff
+40
-12
@@ -6,6 +6,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -170,7 +171,7 @@ private:
|
||||
} // namespace aurora
|
||||
|
||||
namespace aurora::gfx {
|
||||
inline constexpr bool UseTextureBuffer = false;
|
||||
inline constexpr bool UseTextureBuffer = true;
|
||||
inline constexpr uint64_t UniformBufferSize = 25165824; // 24mb
|
||||
inline constexpr uint64_t VertexBufferSize = 3145728; // 3mb
|
||||
inline constexpr uint64_t IndexBufferSize = 1048576; // 1mb
|
||||
@@ -215,25 +216,52 @@ using webgpu::Viewport;
|
||||
|
||||
struct TextureRef;
|
||||
using TextureHandle = std::shared_ptr<TextureRef>;
|
||||
using EndFrameCallback = std::function<void(wgpu::CommandEncoder&)>;
|
||||
|
||||
enum class ShaderType : uint8_t {
|
||||
Clear = 0,
|
||||
GX = 1,
|
||||
Rml = 2,
|
||||
};
|
||||
|
||||
void initialize();
|
||||
void shutdown();
|
||||
|
||||
bool begin_frame();
|
||||
void end_frame(const wgpu::CommandEncoder& cmd);
|
||||
void finish();
|
||||
void end_frame(EndFrameCallback callback);
|
||||
uint32_t current_frame() noexcept;
|
||||
void render(wgpu::CommandEncoder& cmd);
|
||||
void render_pass(const wgpu::RenderPassEncoder& pass, uint32_t idx);
|
||||
void after_submit() noexcept;
|
||||
void map_staging_buffer();
|
||||
void gpu_synchronize();
|
||||
void resolve_pass(TextureHandle texture, ClipRect rect, bool clearColor, bool clearAlpha, bool clearDepth,
|
||||
Vec4<float> clearColorValue, float clearDepthValue, GXTexFmt resolveFormat = GX_TF_RGBA8);
|
||||
|
||||
struct ColorPassDescriptor {
|
||||
const char* label = nullptr;
|
||||
wgpu::TextureView colorView;
|
||||
wgpu::TextureView resolveView;
|
||||
wgpu::TextureView depthStencilView;
|
||||
wgpu::Extent3D targetSize;
|
||||
uint32_t sampleCount = 1;
|
||||
wgpu::LoadOp colorLoadOp = wgpu::LoadOp::Clear;
|
||||
wgpu::StoreOp colorStoreOp = wgpu::StoreOp::Store;
|
||||
wgpu::Color clearColor{0.f, 0.f, 0.f, 0.f};
|
||||
bool hasDepth = false;
|
||||
wgpu::LoadOp depthLoadOp = wgpu::LoadOp::Undefined;
|
||||
wgpu::StoreOp depthStoreOp = wgpu::StoreOp::Undefined;
|
||||
float depthClearValue = 0.f;
|
||||
bool hasStencil = false;
|
||||
wgpu::LoadOp stencilLoadOp = wgpu::LoadOp::Undefined;
|
||||
wgpu::StoreOp stencilStoreOp = wgpu::StoreOp::Undefined;
|
||||
uint32_t stencilClearValue = 0;
|
||||
bool observable = true;
|
||||
};
|
||||
|
||||
void begin_color_pass(const ColorPassDescriptor& desc);
|
||||
void end_color_pass();
|
||||
void queue_texture_copy(wgpu::TexelCopyTextureInfo src, wgpu::TexelCopyTextureInfo dst, wgpu::Extent3D size);
|
||||
|
||||
void begin_offscreen(uint32_t width, uint32_t height);
|
||||
void end_offscreen();
|
||||
bool is_offscreen() noexcept;
|
||||
@@ -245,15 +273,15 @@ struct ConvRequest;
|
||||
} // namespace tex_palette_conv
|
||||
void queue_palette_conv(tex_palette_conv::ConvRequest req);
|
||||
|
||||
Range push_verts(const uint8_t* data, size_t length);
|
||||
Range push_verts(const uint8_t* data, size_t length, size_t alignment);
|
||||
template <typename T>
|
||||
static Range push_verts(ArrayRef<T> data) {
|
||||
return push_verts(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T));
|
||||
static Range push_verts(ArrayRef<T> data, size_t alignment) {
|
||||
return push_verts(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T), alignment);
|
||||
}
|
||||
Range push_indices(const uint8_t* data, size_t length);
|
||||
Range push_indices(const uint8_t* data, size_t length, size_t alignment);
|
||||
template <typename T>
|
||||
static Range push_indices(ArrayRef<T> data) {
|
||||
return push_indices(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T));
|
||||
static Range push_indices(ArrayRef<T> data, size_t alignment) {
|
||||
return push_indices(reinterpret_cast<const uint8_t*>(data.data()), data.size() * sizeof(T), alignment);
|
||||
}
|
||||
Range push_uniform(const uint8_t* data, size_t length);
|
||||
template <typename T>
|
||||
@@ -287,9 +315,9 @@ PipelineRef pipeline_ref(const PipelineConfig& config);
|
||||
bool bind_pipeline(PipelineRef ref, const wgpu::RenderPassEncoder& pass);
|
||||
|
||||
BindGroupRef bind_group_ref(const WGPUBindGroupDescriptor& descriptor);
|
||||
wgpu::BindGroup& find_bind_group(BindGroupRef id);
|
||||
wgpu::BindGroup find_bind_group(BindGroupRef id);
|
||||
|
||||
wgpu::Sampler& sampler_ref(const wgpu::SamplerDescriptor& descriptor);
|
||||
wgpu::Sampler sampler_ref(const wgpu::SamplerDescriptor& descriptor);
|
||||
|
||||
uint32_t align_uniform(uint32_t value);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../dolphin/vi/vi_internal.hpp"
|
||||
#include "../gx/gx.hpp"
|
||||
#include "../gfx/render_worker.hpp"
|
||||
#include "../webgpu/gpu.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -330,12 +331,6 @@ bool read_latest(uint16_t x, uint16_t y, uint32_t& z) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
void poll() noexcept {
|
||||
if (g_instance) {
|
||||
g_instance.ProcessEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void encode_frame_snapshot(const wgpu::CommandEncoder& cmd, const wgpu::TextureView& depthView,
|
||||
wgpu::Extent3D sourceSize, uint32_t msaaSamples) noexcept {
|
||||
ZoneScoped;
|
||||
@@ -375,6 +370,7 @@ void encode_frame_snapshot(const wgpu::CommandEncoder& cmd, const wgpu::TextureV
|
||||
byteSize = slot->byteSize;
|
||||
}
|
||||
|
||||
ASSERT(render_worker::is_worker_thread(), "Depth peek queue write must run on the render worker");
|
||||
g_queue.WriteBuffer(paramsBuffer, 0, ¶ms, sizeof(params));
|
||||
|
||||
const std::array bindGroupEntries{
|
||||
|
||||
@@ -11,7 +11,6 @@ void shutdown();
|
||||
|
||||
void request_snapshot() noexcept;
|
||||
bool read_latest(uint16_t x, uint16_t y, uint32_t& z) noexcept;
|
||||
void poll() noexcept;
|
||||
|
||||
void encode_frame_snapshot(const wgpu::CommandEncoder& cmd, const wgpu::TextureView& depthView,
|
||||
wgpu::Extent3D sourceSize, uint32_t msaaSamples) noexcept;
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#include "clear.hpp"
|
||||
#include "../fs_helper.hpp"
|
||||
#include "../gx/pipeline.hpp"
|
||||
#ifdef AURORA_ENABLE_RMLUI
|
||||
#include "../rmlui/pipeline.hpp"
|
||||
#endif
|
||||
#include "../sqlite_utils.hpp"
|
||||
#include "../webgpu/gpu.hpp"
|
||||
|
||||
@@ -140,12 +143,18 @@ static PendingPipeline* touch_pending_pipeline(PipelineRef hash, bool prioritize
|
||||
return &g_priorityPipelines.back();
|
||||
}
|
||||
|
||||
static PipelineRef g_lastPipelineRef = std::numeric_limits<PipelineRef>::max();
|
||||
|
||||
template <typename PipelineConfig>
|
||||
static PipelineRef find_pipeline_impl(ShaderType type, const PipelineConfig& config, NewPipelineCallback&& cb,
|
||||
bool persist, std::optional<uint32_t> firstFrameUsedOverride) {
|
||||
ZoneScoped;
|
||||
|
||||
const PipelineRef hash = xxh3_hash(config, static_cast<HashType>(type));
|
||||
if (hash == g_lastPipelineRef) {
|
||||
return g_lastPipelineRef;
|
||||
}
|
||||
g_lastPipelineRef = hash;
|
||||
const uint32_t firstFrameUsed = firstFrameUsedOverride.value_or(current_frame());
|
||||
bool notifyWorker = false;
|
||||
std::optional<PipelineCacheWrite> cacheWrite;
|
||||
@@ -352,7 +361,18 @@ static void prune_old_pipeline_cache_versions() {
|
||||
if (ret != SQLITE_OK) {
|
||||
Log.error("Failed to prune GX pipeline cache rows: {}", sqlite3_errmsg(g_pipelineCacheDb));
|
||||
pipeline_cache_abort();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef AURORA_ENABLE_RMLUI
|
||||
const auto rmlDelete = fmt::format("DELETE FROM pipeline_cache WHERE type = {} AND config_version < {}",
|
||||
underlying(ShaderType::Rml), rmlui::RmlPipelineConfigVersion);
|
||||
ret = sqlite::exec(g_pipelineCacheDb, rmlDelete.c_str());
|
||||
if (ret != SQLITE_OK) {
|
||||
Log.error("Failed to prune RmlUi pipeline cache rows: {}", sqlite3_errmsg(g_pipelineCacheDb));
|
||||
pipeline_cache_abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool write_pipeline_cache_record(const PipelineCacheWrite& write) {
|
||||
@@ -552,6 +572,13 @@ static void load_pipeline_cache() {
|
||||
return;
|
||||
}
|
||||
load_pipeline_cache_entries<gx::PipelineConfig>(ShaderType::GX, gx::GXPipelineConfigVersion, gx::create_pipeline);
|
||||
#ifdef AURORA_ENABLE_RMLUI
|
||||
if (g_pipelineCacheBroken) {
|
||||
return;
|
||||
}
|
||||
load_pipeline_cache_entries<rmlui::PipelineConfig>(ShaderType::Rml, rmlui::RmlPipelineConfigVersion,
|
||||
rmlui::create_pipeline);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void start_pipeline_cache_writer() {
|
||||
@@ -588,6 +615,13 @@ PipelineRef find_pipeline(ShaderType type, const gx::PipelineConfig& config, New
|
||||
return find_pipeline_impl(type, config, std::move(cb), true, std::nullopt);
|
||||
}
|
||||
|
||||
#ifdef AURORA_ENABLE_RMLUI
|
||||
template <>
|
||||
PipelineRef find_pipeline(ShaderType type, const rmlui::PipelineConfig& config, NewPipelineCallback&& cb) {
|
||||
return find_pipeline_impl(type, config, std::move(cb), true, std::nullopt);
|
||||
}
|
||||
#endif
|
||||
|
||||
void initialize_pipeline_cache() {
|
||||
g_pipelineCacheBroken = false;
|
||||
g_pipelineCacheWriterStop = false;
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace aurora::gx {
|
||||
struct PipelineConfig;
|
||||
} // namespace aurora::gx
|
||||
|
||||
namespace aurora::rmlui {
|
||||
struct PipelineConfig;
|
||||
} // namespace aurora::rmlui
|
||||
|
||||
namespace aurora::gfx {
|
||||
|
||||
using NewPipelineCallback = std::function<wgpu::RenderPipeline()>;
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
#include "render_worker.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
#include <tracy/Tracy.hpp>
|
||||
|
||||
namespace aurora::gfx::render_worker {
|
||||
namespace {
|
||||
constexpr size_t QueueCapacity = 256;
|
||||
constexpr auto IdlePumpInterval = std::chrono::milliseconds{1};
|
||||
|
||||
BoundedQueue g_queue{QueueCapacity};
|
||||
std::thread g_thread;
|
||||
std::atomic_bool g_running = false;
|
||||
std::atomic_size_t g_pendingItems = 0;
|
||||
std::thread::id g_workerThreadId;
|
||||
|
||||
void complete_sync(const std::shared_ptr<SyncState>& sync) {
|
||||
if (!sync) {
|
||||
return;
|
||||
}
|
||||
ZoneScoped;
|
||||
{
|
||||
std::lock_guard lock{sync->mutex};
|
||||
sync->complete = true;
|
||||
}
|
||||
sync->cv.notify_all();
|
||||
}
|
||||
|
||||
void worker_main() {
|
||||
#ifdef TRACY_ENABLE
|
||||
tracy::SetThreadName("Aurora render worker");
|
||||
#endif
|
||||
g_workerThreadId = std::this_thread::get_id();
|
||||
|
||||
while (true) {
|
||||
bool closed = false;
|
||||
auto item = g_queue.pop_for(IdlePumpInterval, closed);
|
||||
if (!item) {
|
||||
if (closed) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->work) {
|
||||
ZoneScopedN("QueueItem work");
|
||||
item->work();
|
||||
}
|
||||
complete_sync(item->sync);
|
||||
g_pendingItems.fetch_sub(1, std::memory_order_acq_rel);
|
||||
if (item->type == ItemType::Shutdown) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_workerThreadId = {};
|
||||
}
|
||||
|
||||
void enqueue(QueueItem item) {
|
||||
ZoneScoped;
|
||||
if (is_worker_thread()) {
|
||||
if (item.work) {
|
||||
item.work();
|
||||
}
|
||||
complete_sync(item.sync);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_running.load(std::memory_order_acquire)) {
|
||||
if (item.work) {
|
||||
item.work();
|
||||
}
|
||||
complete_sync(item.sync);
|
||||
return;
|
||||
}
|
||||
|
||||
g_pendingItems.fetch_add(1, std::memory_order_acq_rel);
|
||||
if (!g_queue.push(std::move(item))) {
|
||||
g_pendingItems.fetch_sub(1, std::memory_order_acq_rel);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
BoundedQueue::BoundedQueue(size_t capacity) : m_capacity(capacity) {}
|
||||
|
||||
bool BoundedQueue::push(QueueItem item) {
|
||||
ZoneScoped;
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_notFull.wait(lock, [&] { return m_closed || m_items.size() < m_capacity; });
|
||||
if (m_closed) {
|
||||
return false;
|
||||
}
|
||||
m_items.emplace_back(std::move(item));
|
||||
lock.unlock();
|
||||
m_notEmpty.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<QueueItem> BoundedQueue::pop_for(std::chrono::milliseconds timeout, bool& closed) {
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_notEmpty.wait_for(lock, timeout, [&] { return m_closed || !m_items.empty(); });
|
||||
closed = m_closed && m_items.empty();
|
||||
if (m_items.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto item = std::move(m_items.front());
|
||||
m_items.pop_front();
|
||||
lock.unlock();
|
||||
m_notFull.notify_one();
|
||||
return item;
|
||||
}
|
||||
|
||||
void BoundedQueue::close() {
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
m_closed = true;
|
||||
}
|
||||
m_notEmpty.notify_all();
|
||||
m_notFull.notify_all();
|
||||
}
|
||||
|
||||
void BoundedQueue::reset() {
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
m_items.clear();
|
||||
m_closed = false;
|
||||
}
|
||||
m_notFull.notify_all();
|
||||
}
|
||||
|
||||
size_t BoundedQueue::size() const {
|
||||
std::lock_guard lock{m_mutex};
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
FrameSlotPool::FrameSlotPool(size_t slotCount) : m_freeSlots(slotCount, true) {}
|
||||
|
||||
size_t FrameSlotPool::acquire() {
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_cv.wait(lock, [&] {
|
||||
for (const bool free : m_freeSlots) {
|
||||
if (free) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
for (size_t i = 0; i < m_freeSlots.size(); ++i) {
|
||||
if (m_freeSlots[i]) {
|
||||
m_freeSlots[i] = false;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<size_t> FrameSlotPool::try_acquire() {
|
||||
std::lock_guard lock{m_mutex};
|
||||
for (size_t i = 0; i < m_freeSlots.size(); ++i) {
|
||||
if (m_freeSlots[i]) {
|
||||
m_freeSlots[i] = false;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void FrameSlotPool::release(size_t slot) {
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
if (slot < m_freeSlots.size()) {
|
||||
m_freeSlots[slot] = true;
|
||||
}
|
||||
}
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
void FrameSlotPool::reset() {
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
std::fill(m_freeSlots.begin(), m_freeSlots.end(), true);
|
||||
}
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
size_t FrameSlotPool::free_count() const {
|
||||
std::lock_guard lock{m_mutex};
|
||||
return static_cast<size_t>(std::ranges::count(m_freeSlots, true));
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
if (g_running.exchange(true, std::memory_order_acq_rel)) {
|
||||
return;
|
||||
}
|
||||
g_queue.reset();
|
||||
g_pendingItems.store(0, std::memory_order_release);
|
||||
g_thread = std::thread(worker_main);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
if (!g_running.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
enqueue({
|
||||
.type = ItemType::Shutdown,
|
||||
});
|
||||
if (g_thread.joinable()) {
|
||||
g_thread.join();
|
||||
}
|
||||
g_running.store(false, std::memory_order_release);
|
||||
g_queue.close();
|
||||
g_queue.reset();
|
||||
g_pendingItems.store(0, std::memory_order_release);
|
||||
}
|
||||
|
||||
void enqueue_begin_frame(uint64_t frameId, WorkCallback work) {
|
||||
enqueue({
|
||||
.type = ItemType::BeginFrame,
|
||||
.frameId = frameId,
|
||||
.work = std::move(work),
|
||||
});
|
||||
}
|
||||
|
||||
void enqueue_encode_pass(uint64_t frameId, uint32_t passIndex, WorkCallback work) {
|
||||
enqueue({
|
||||
.type = ItemType::EncodePass,
|
||||
.frameId = frameId,
|
||||
.passIndex = passIndex,
|
||||
.work = std::move(work),
|
||||
});
|
||||
}
|
||||
|
||||
void enqueue_end_frame(uint64_t frameId, WorkCallback work) {
|
||||
enqueue({
|
||||
.type = ItemType::EndFrame,
|
||||
.frameId = frameId,
|
||||
.work = std::move(work),
|
||||
});
|
||||
}
|
||||
|
||||
void enqueue_work(WorkCallback work) {
|
||||
enqueue({
|
||||
.type = ItemType::Sync,
|
||||
.work = std::move(work),
|
||||
});
|
||||
}
|
||||
|
||||
void synchronize() {
|
||||
if (is_worker_thread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneScoped;
|
||||
auto sync = std::make_shared<SyncState>();
|
||||
enqueue({
|
||||
.type = ItemType::Sync,
|
||||
.sync = sync,
|
||||
});
|
||||
std::unique_lock lock{sync->mutex};
|
||||
sync->cv.wait(lock, [&] { return sync->complete; });
|
||||
}
|
||||
|
||||
bool is_worker_thread() noexcept { return g_workerThreadId == std::this_thread::get_id(); }
|
||||
|
||||
bool is_idle() noexcept { return g_pendingItems.load(std::memory_order_acquire) == 0; }
|
||||
|
||||
} // namespace aurora::gfx::render_worker
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace aurora::gfx::render_worker {
|
||||
|
||||
enum class ItemType : uint8_t {
|
||||
BeginFrame,
|
||||
EncodePass,
|
||||
EndFrame,
|
||||
Sync,
|
||||
Shutdown,
|
||||
};
|
||||
|
||||
using WorkCallback = std::function<void()>;
|
||||
|
||||
struct SyncState {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool complete = false;
|
||||
};
|
||||
|
||||
struct QueueItem {
|
||||
ItemType type = ItemType::Sync;
|
||||
uint64_t frameId = 0;
|
||||
uint32_t passIndex = 0;
|
||||
WorkCallback work;
|
||||
std::shared_ptr<SyncState> sync;
|
||||
};
|
||||
|
||||
class BoundedQueue {
|
||||
public:
|
||||
explicit BoundedQueue(size_t capacity);
|
||||
|
||||
bool push(QueueItem item);
|
||||
std::optional<QueueItem> pop_for(std::chrono::milliseconds timeout, bool& closed);
|
||||
void close();
|
||||
void reset();
|
||||
[[nodiscard]] size_t size() const;
|
||||
|
||||
private:
|
||||
size_t m_capacity = 0;
|
||||
mutable std::mutex m_mutex;
|
||||
std::condition_variable m_notEmpty;
|
||||
std::condition_variable m_notFull;
|
||||
std::deque<QueueItem> m_items;
|
||||
bool m_closed = false;
|
||||
};
|
||||
|
||||
class FrameSlotPool {
|
||||
public:
|
||||
explicit FrameSlotPool(size_t slotCount);
|
||||
|
||||
size_t acquire();
|
||||
std::optional<size_t> try_acquire();
|
||||
void release(size_t slot);
|
||||
void reset();
|
||||
[[nodiscard]] size_t free_count() const;
|
||||
|
||||
private:
|
||||
mutable std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::vector<bool> m_freeSlots;
|
||||
};
|
||||
|
||||
void initialize();
|
||||
void shutdown();
|
||||
|
||||
void enqueue_begin_frame(uint64_t frameId, WorkCallback work);
|
||||
void enqueue_encode_pass(uint64_t frameId, uint32_t passIndex, WorkCallback work);
|
||||
void enqueue_end_frame(uint64_t frameId, WorkCallback work);
|
||||
void enqueue_work(WorkCallback work);
|
||||
void synchronize();
|
||||
|
||||
bool is_worker_thread() noexcept;
|
||||
bool is_idle() noexcept;
|
||||
|
||||
} // namespace aurora::gfx::render_worker
|
||||
+4
-14
@@ -133,13 +133,8 @@ TextureHandle new_static_texture_2d(uint32_t width, uint32_t height, uint32_t mi
|
||||
.mipLevel = mip,
|
||||
};
|
||||
if constexpr (UseTextureBuffer) {
|
||||
const auto range = push_texture_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks);
|
||||
const wgpu::TexelCopyBufferLayout dataLayout{
|
||||
.offset = range.offset,
|
||||
.bytesPerRow = bytesPerRow,
|
||||
.rowsPerImage = heightBlocks,
|
||||
};
|
||||
g_textureUploads.emplace_back(dataLayout, std::move(dstView), physicalSize);
|
||||
queue_texture_upload_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks, std::move(dstView),
|
||||
physicalSize);
|
||||
} else {
|
||||
const wgpu::TexelCopyBufferLayout dataLayout{
|
||||
.bytesPerRow = bytesPerRow,
|
||||
@@ -288,13 +283,8 @@ void write_texture(TextureRef& ref, ArrayRef<uint8_t> data) noexcept {
|
||||
.mipLevel = mip,
|
||||
};
|
||||
if constexpr (UseTextureBuffer) {
|
||||
const auto range = push_texture_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks);
|
||||
const wgpu::TexelCopyBufferLayout dataLayout{
|
||||
.offset = range.offset,
|
||||
.bytesPerRow = bytesPerRow,
|
||||
.rowsPerImage = heightBlocks,
|
||||
};
|
||||
g_textureUploads.emplace_back(dataLayout, std::move(dstView), physicalSize);
|
||||
queue_texture_upload_data(data.data() + offset, dataSize, bytesPerRow, heightBlocks, std::move(dstView),
|
||||
physicalSize);
|
||||
} else {
|
||||
const wgpu::TexelCopyBufferLayout dataLayout{
|
||||
.bytesPerRow = bytesPerRow,
|
||||
|
||||
+7
-1
@@ -10,11 +10,17 @@ struct TextureUpload {
|
||||
wgpu::TexelCopyBufferLayout layout;
|
||||
wgpu::TexelCopyTextureInfo tex;
|
||||
wgpu::Extent3D size;
|
||||
wgpu::Buffer buffer;
|
||||
|
||||
TextureUpload(wgpu::TexelCopyBufferLayout layout, wgpu::TexelCopyTextureInfo tex, wgpu::Extent3D size) noexcept
|
||||
: layout(layout), tex(std::move(tex)), size(size) {}
|
||||
TextureUpload(wgpu::TexelCopyBufferLayout layout, wgpu::TexelCopyTextureInfo tex, wgpu::Extent3D size,
|
||||
wgpu::Buffer buffer) noexcept
|
||||
: layout(layout), tex(std::move(tex)), size(size), buffer(std::move(buffer)) {}
|
||||
};
|
||||
extern std::vector<TextureUpload> g_textureUploads;
|
||||
void queue_texture_upload(TextureUpload upload);
|
||||
void queue_texture_upload_data(const uint8_t* data, size_t length, uint32_t bytesPerRow, uint32_t rowsPerImage,
|
||||
wgpu::TexelCopyTextureInfo tex, wgpu::Extent3D size);
|
||||
|
||||
struct TextureFormatInfo {
|
||||
uint8_t blockWidth;
|
||||
|
||||
@@ -558,6 +558,7 @@ static ByteBuffer BuildRGBA8FromBC1(uint32_t width, uint32_t height, uint32_t mi
|
||||
}
|
||||
|
||||
ConvertedTexture convert_texture(u32 format, uint32_t width, uint32_t height, uint32_t mips, ArrayRef<uint8_t> data) {
|
||||
ZoneScoped;
|
||||
ByteBuffer converted;
|
||||
switch (format) {
|
||||
DEFAULT_FATAL("convert_texture: unknown texture format {}", format);
|
||||
|
||||
@@ -1562,32 +1562,47 @@ static void handle_draw(u8 cmd, const u8* data, u32& pos, u32 size, bool bigEndi
|
||||
if (pos + totalVtxBytes > size)
|
||||
UNLIKELY { handle_draw_overrun(totalVtxBytes, data, pos, size); }
|
||||
|
||||
// Push raw vertex data to buffer
|
||||
gfx::Range vertRange = gfx::push_verts(data + pos, totalVtxBytes);
|
||||
auto* lastDraw = !g_gxState.stateDirty ? gfx::get_last_draw_command<DrawData>() : nullptr;
|
||||
const bool canMerge = lastDraw != nullptr && prim != GX_LINES && prim != GX_LINESTRIP && prim != GX_POINTS &&
|
||||
lastDraw->instanceCount == 1;
|
||||
|
||||
// Push raw vertex data to buffer. Merged draws must remain byte-contiguous with the previous range.
|
||||
gfx::Range vertRange = gfx::push_verts(data + pos, totalVtxBytes, canMerge ? 0 : 4);
|
||||
pos += totalVtxBytes;
|
||||
|
||||
// Try to merge with previous draw call
|
||||
if (!g_gxState.stateDirty) LIKELY {
|
||||
auto* lastDraw = gfx::get_last_draw_command<DrawData>();
|
||||
if (canMerge) {
|
||||
// Only if the previous draw call was a single instance draw (no lines/points handling)
|
||||
if (lastDraw != nullptr && prim != GX_LINES && prim != GX_LINESTRIP && prim != GX_POINTS &&
|
||||
lastDraw->instanceCount == 1) LIKELY {
|
||||
u32 numIndices = prepare_idx_buffer(handle_draw_idx_buf, prim, lastDraw->vtxCount, vtxCount);
|
||||
gfx::Range idxRange = gfx::push_indices(handle_draw_idx_buf.data(), handle_draw_idx_buf.size());
|
||||
u32 numIndices = 0;
|
||||
gfx::Range idxRange;
|
||||
const bool hadIndexRange = lastDraw->idxRange.size != 0;
|
||||
if (lastDraw->indexCount == 0 && prim != GX_TRIANGLES) {
|
||||
// Generate triangle index buffer for previous draw
|
||||
lastDraw->indexCount = prepare_idx_buffer(handle_draw_idx_buf, GX_TRIANGLES, 0, lastDraw->vtxCount);
|
||||
}
|
||||
if (lastDraw->indexCount != 0) {
|
||||
numIndices += prepare_idx_buffer(handle_draw_idx_buf, prim, lastDraw->vtxCount, vtxCount);
|
||||
idxRange = gfx::push_indices(handle_draw_idx_buf.data(), handle_draw_idx_buf.size(), hadIndexRange ? 0 : 4);
|
||||
handle_draw_idx_buf.clear();
|
||||
CHECK(lastDraw->vertRange.offset + lastDraw->vertRange.size == vertRange.offset,
|
||||
"Non-consecutive vertex ranges ({} < {})", lastDraw->vertRange.offset + lastDraw->vertRange.size,
|
||||
vertRange.offset);
|
||||
}
|
||||
CHECK(lastDraw->vertRange.offset + lastDraw->vertRange.size == vertRange.offset,
|
||||
"Non-consecutive vertex ranges ({} < {})", lastDraw->vertRange.offset + lastDraw->vertRange.size,
|
||||
vertRange.offset);
|
||||
if (hadIndexRange) {
|
||||
CHECK(lastDraw->idxRange.offset + lastDraw->idxRange.size == idxRange.offset,
|
||||
"Non-consecutive index ranges ({} < {})", lastDraw->idxRange.offset + lastDraw->idxRange.size,
|
||||
idxRange.offset);
|
||||
lastDraw->vertRange.size += vertRange.size;
|
||||
lastDraw->idxRange.size += idxRange.size;
|
||||
lastDraw->vtxCount += vtxCount;
|
||||
lastDraw->indexCount += numIndices;
|
||||
++gfx::g_mergedDrawCallCount;
|
||||
return;
|
||||
}
|
||||
lastDraw->vertRange.size += vertRange.size;
|
||||
if (lastDraw->idxRange.size == 0) {
|
||||
lastDraw->idxRange = idxRange;
|
||||
} else {
|
||||
lastDraw->idxRange.size += idxRange.size;
|
||||
}
|
||||
lastDraw->vtxCount += vtxCount;
|
||||
lastDraw->indexCount += numIndices;
|
||||
++gfx::g_mergedDrawCallCount;
|
||||
return;
|
||||
}
|
||||
|
||||
handle_draw_unmerged(prim, fmt, vtxCount, vertRange);
|
||||
@@ -1600,11 +1615,11 @@ static void handle_draw_unmerged(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount, g
|
||||
u32 numIndices = 0;
|
||||
gfx::Range idxRange;
|
||||
|
||||
{
|
||||
ByteBuffer idxBuf;
|
||||
auto& realBuf = vtxCount < 1000 ? handle_draw_unmerged_idxBuf : idxBuf;
|
||||
if (prim != GX_TRIANGLES) {
|
||||
ZoneScopedN("build idx buffer");
|
||||
auto& realBuf = handle_draw_unmerged_idxBuf;
|
||||
numIndices = prepare_idx_buffer(realBuf, prim, 0, vtxCount);
|
||||
idxRange = gfx::push_indices(realBuf.data(), realBuf.size());
|
||||
idxRange = gfx::push_indices(realBuf.data(), realBuf.size(), 4);
|
||||
realBuf.clear();
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -29,6 +29,10 @@ void render(const DrawData& data, const wgpu::RenderPassEncoder& pass) {
|
||||
const wgpu::Color color{0.f, 0.f, 0.f, data.dstAlpha / 255.f};
|
||||
pass.SetBlendConstant(&color);
|
||||
}
|
||||
pass.DrawIndexed(data.indexCount, data.instanceCount);
|
||||
if (data.indexCount == 0) {
|
||||
pass.Draw(data.vtxCount, data.instanceCount);
|
||||
} else {
|
||||
pass.DrawIndexed(data.indexCount, data.instanceCount);
|
||||
}
|
||||
}
|
||||
} // namespace aurora::gx
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user