ArtifactEngine/Client/Graphics/Graphics.cpp
2026-04-06 19:17:05 -04:00

168 lines
6 KiB
C++

#include "Graphics.h"
#include "../Platform/Window.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
namespace Artifact {
void Graphics::init() {
instance = wgpuCreateInstance(nullptr);
window = client->subsystem<WindowImpl>();
surface = window->createWGPUSurface(instance);
WGPURequestAdapterOptions opts {
.compatibleSurface = surface
};
wgpuInstanceRequestAdapter(instance, &opts, {
.mode = WGPUCallbackMode_AllowProcessEvents,
.callback = [](auto status, auto adapter, auto msg, auto userdata, auto) {
if (status == WGPURequestAdapterStatus_Success) {
static_cast<Graphics *>(userdata)->adapter = adapter;
} else {
throw std::runtime_error("Failed to get wgpu adapter.");
}
},
.userdata1 = this
});
// To avoid dealing with concurrency, we just block until the adapter is available (or the program errors).
while (!adapter) wgpuInstanceProcessEvents(instance);
WGPUDeviceDescriptor desc {
.uncapturedErrorCallbackInfo = {
.callback = [](auto device, auto type, auto msg, auto, auto) {
const char * message = (msg.data ? reinterpret_cast<const char *>(msg.data) : "No message");
fprintf(stderr, "Uncaptured WebGPU Error (type %d): %s\n", type, message);
}}
};
wgpuAdapterRequestDevice(adapter, &desc, {
.mode = WGPUCallbackMode_AllowProcessEvents,
.callback = [](auto status, auto device, auto msg, auto userdata, auto) {
if (status == WGPURequestDeviceStatus_Success) {
static_cast<Graphics *>(userdata)->device = device;
} else {
throw std::runtime_error("Failed to get wgpu device.");
}
},
.userdata1 = this
});
while (!device) wgpuInstanceProcessEvents(instance);
WGPUTextureDescriptor depthDesc = {};
depthDesc.dimension = WGPUTextureDimension_2D;
depthDesc.size.width = 1080;
depthDesc.size.height = 640;
depthDesc.size.depthOrArrayLayers = 1;
depthDesc.format = WGPUTextureFormat_Depth32Float;
depthDesc.usage = WGPUTextureUsage_RenderAttachment;
depthDesc.mipLevelCount = 1;
depthDesc.sampleCount = 1;
depthTexture = wgpuDeviceCreateTexture(device, &depthDesc);
depthTextureView = wgpuTextureCreateView(depthTexture, nullptr);
queue = wgpuDeviceGetQueue(device);
WGPUSurfaceConfiguration surfaceConfig = {};
surfaceConfig.device = device;
surfaceConfig.format = WGPUTextureFormat_BGRA8Unorm; // FIXME: Auto-detect
surfaceConfig.usage = WGPUTextureUsage_RenderAttachment;
surfaceConfig.width = 1080;
surfaceConfig.height = 640;
surfaceConfig.presentMode = WGPUPresentMode_Fifo;
wgpuSurfaceConfigure(surface, &surfaceConfig);
for (auto & system : subsystems) {
system->graphics = this;
printf("Graphics: %p", system->graphics);
system->init();
system->reload();
}
}
void Graphics::deinit() {
for (auto & system : subsystems) {
system->deinit();
}
wgpuTextureDestroy(depthTexture);
wgpuQueueRelease(queue);
wgpuDeviceDestroy(device);
wgpuAdapterRelease(adapter);
wgpuSurfaceRelease(surface);
wgpuInstanceRelease(instance);
}
void Graphics::render() {
WGPUSurfaceTexture surfaceTexture;
wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture);
if (surfaceTexture.status == WGPUSurfaceGetCurrentTextureStatus_Error || !surfaceTexture.texture) {
throw std::runtime_error("Failed to get surface texture");
}
WGPUTextureView nextTexture = wgpuTextureCreateView(surfaceTexture.texture, nullptr);
if (!nextTexture) {
throw std::runtime_error("Failed to create texture view");
}
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
for (auto & system : subsystems) {
system->render(nextTexture, encoder);
}
WGPUCommandBufferDescriptor cmdBufferDesc = {};
cmdBufferDesc.label = WGPUStringView{"Command buffer", WGPU_STRLEN};
WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(encoder, nullptr);
wgpuQueueSubmit(queue, 1, &cmdBuffer);
#ifndef WASM_BUILD
wgpuSurfacePresent(surface);
#endif
wgpuTextureViewRelease(nextTexture);
wgpuTextureRelease(surfaceTexture.texture);
}
WGPUTexture Graphics::createTextureFromData(const void* data, uint32_t width, uint32_t height) {
WGPUTextureDescriptor textureDesc = {};
textureDesc.dimension = WGPUTextureDimension_2D;
textureDesc.size.width = width;
textureDesc.size.height = height;
textureDesc.size.depthOrArrayLayers = 1;
textureDesc.format = WGPUTextureFormat_RGBA8Unorm;
textureDesc.usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst;
textureDesc.mipLevelCount = 1;
textureDesc.sampleCount = 1;
auto texture = wgpuDeviceCreateTexture(device, &textureDesc);
// WGPUTextureViewDescriptor viewDesc = {};
// viewDesc.format = WGPUTextureFormat_RGBA8Unorm;
// viewDesc.dimension = WGPUTextureViewDimension_2D;
// viewDesc.mipLevelCount = 1;
// viewDesc.arrayLayerCount = 1;
// tex->view = wgpuTextureCreateView(tex->texture, &viewDesc);
WGPUTexelCopyTextureInfo destination = {};
destination.texture = texture;
destination.mipLevel = 0;
destination.origin = {0, 0, 0};
destination.aspect = WGPUTextureAspect_All;
WGPUTexelCopyBufferLayout layout = {};
layout.offset = 0;
layout.bytesPerRow = width * 4;
layout.rowsPerImage = height;
WGPUExtent3D writeSize = {width, height, 1};
wgpuQueueWriteTexture(queue, &destination, data, width * height * 4, &layout, &writeSize);
return texture;
}
WGPUTexture Graphics::createTextureFromFile(const char* file) {
int width, height, channels;
auto data = stbi_load(file, &width, &height, &channels, 4);
auto out = createTextureFromData(data, width, height);
stbi_image_free(data);
return out;
}
}