#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(); 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(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(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(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; } }