168 lines
6 KiB
C++
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 = engine->subsystem<Window>();
|
|
|
|
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;
|
|
}
|
|
|
|
}
|