载入中...
搜索中...
未找到
GraphicsReadback.cpp
浏览该文件的文档.
1#include <algorithm>
2#include <cmath>
3#include <cstring>
4#include <fstream>
5#include <memory>
6#include <vector>
7#include "common/Exception.h"
12#include "image/ImageData.h"
13#if defined(__EMSCRIPTEN__)
14#include <emscripten/emscripten.h>
15#endif
16namespace eve::graphics::webgpu {
17void Graphics::recordPresentedReadback(wgpu::CommandEncoder &encoder, const wgpu::Texture &surfaceTexture) {
18 if (!surfaceCanCopySrc) throw Exception("WebGPU: surface does not support screen readback");
19 const int w = static_cast<int>(surfaceTexture.GetWidth());
20 const int h = static_cast<int>(surfaceTexture.GetHeight());
21 if (!presentedReadback || presentedReadbackW != w || presentedReadbackH != h ||
22 presentedReadbackFormat != surfaceFormat) {
23 WGPUTextureDescriptor desc{};
24 desc.label = sv("eve_presented_readback");
25 desc.dimension = WGPUTextureDimension_2D;
26 desc.size = {static_cast<uint32_t>(w), static_cast<uint32_t>(h), 1};
27 desc.format = surfaceFormat;
28 desc.mipLevelCount = 1;
29 desc.sampleCount = 1;
30 desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_CopySrc;
31 presentedReadback = device.CreateTexture(reinterpret_cast<const wgpu::TextureDescriptor *>(&desc));
32 presentedReadbackW = w;
33 presentedReadbackH = h;
34 presentedReadbackFormat = surfaceFormat;
35 }
36 WGPUTexelCopyTextureInfo from{}, to{};
37 from.texture = surfaceTexture.Get();
38 from.aspect = WGPUTextureAspect_All;
39 to.texture = presentedReadback.Get();
40 to.aspect = WGPUTextureAspect_All;
41 WGPUExtent3D extent{static_cast<uint32_t>(w), static_cast<uint32_t>(h), 1};
42 encoder.CopyTextureToTexture(reinterpret_cast<const wgpu::TexelCopyTextureInfo *>(&from),
43 reinterpret_cast<const wgpu::TexelCopyTextureInfo *>(&to),
44 reinterpret_cast<const wgpu::Extent3D *>(&extent));
45}
46
48 if (activeCanvas) return getPixelImpl(static_cast<OffscreenCanvas *>(activeCanvas), x, y);
49 if ((screenReadbackEnabled && presentedReadback) || !sceneColorSlots.empty()) {
50 return getPixelImpl(nullptr, x, y);
51 }
52 return clearColor;
53}
54
56 if (activeCanvas) return newImageDataImpl(static_cast<OffscreenCanvas *>(activeCanvas));
57 if ((screenReadbackEnabled && presentedReadback) || !sceneColorSlots.empty()) return newImageDataImpl(nullptr);
58 return new image::ImageData(1, 1, "RGBA8");
59}
60
61// ---------------------------------------------------------------------------
62// Readback
63// ---------------------------------------------------------------------------
64
65namespace {
66
67bool copyTextureToCpu(wgpu::Instance &instance, wgpu::Device &device, wgpu::Queue &queue, wgpu::Texture src, int width,
68 int height, std::vector<uint8_t> &outRgba, int bytesPerPixel = 4) {
69 if (!src) return false;
70 uint64_t bytesPerRow = static_cast<uint64_t>(width) * bytesPerPixel;
71 bytesPerRow = (bytesPerRow + 255) / 256 * 256; // copy alignment 256
72 uint64_t size = bytesPerRow * height;
73
74 WGPUBufferDescriptor bd{};
75 bd.label = sv("eve_readback");
76 bd.size = size;
77 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead;
78 bd.mappedAtCreation = false;
79 wgpu::Buffer dst = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor *>(&bd));
80
81 wgpu::CommandEncoder enc = device.CreateCommandEncoder();
82 WGPUTexelCopyTextureInfo from{};
83 from.texture = src.Get();
84 from.mipLevel = 0;
85 from.aspect = WGPUTextureAspect_All;
86 from.origin = {0, 0, 0};
87 WGPUTexelCopyBufferInfo to{};
88 to.buffer = dst.Get();
89 to.layout.offset = 0;
90 to.layout.bytesPerRow = static_cast<uint32_t>(bytesPerRow);
91 to.layout.rowsPerImage = static_cast<uint32_t>(height);
92 WGPUExtent3D extent{static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1};
93 enc.CopyTextureToBuffer(reinterpret_cast<const wgpu::TexelCopyTextureInfo *>(&from),
94 reinterpret_cast<const wgpu::TexelCopyBufferInfo *>(&to),
95 reinterpret_cast<const wgpu::Extent3D *>(&extent));
96 wgpu::CommandBuffer cmd = enc.Finish();
97 queue.Submit(1, &cmd);
98
99 struct MapState {
100 bool done = false;
101 bool success = false;
102 WGPUMapAsyncStatus status = WGPUMapAsyncStatus_Force32;
103 } map;
104 WGPUBufferMapCallbackInfo cbInfo{};
105#if defined(__EMSCRIPTEN__)
106 cbInfo.mode = WGPUCallbackMode_AllowProcessEvents;
107#else
108 cbInfo.mode = WGPUCallbackMode_WaitAnyOnly;
109#endif
110 cbInfo.callback = [](WGPUMapAsyncStatus status, WGPUStringView /*message*/, void *userdata1, void * /*userdata2*/) {
111 auto *state = static_cast<MapState *>(userdata1);
112 state->status = status;
113 state->success = status == WGPUMapAsyncStatus_Success;
114 state->done = true;
115 };
116 cbInfo.userdata1 = &map;
117 WGPUFuture future = wgpuBufferMapAsync(dst.Get(), WGPUMapMode_Read, 0, size, cbInfo);
118
119#if defined(__EMSCRIPTEN__)
120 int guard = 0;
121 while (!map.done && guard < 2000) {
122 emscripten_sleep(0);
123 wgpuInstanceProcessEvents(instance.Get());
124 ++guard;
125 }
126#else
127 WGPUFutureWaitInfo waitInfo{};
128 waitInfo.future = future;
129 (void)wgpuInstanceWaitAny(instance.Get(), 1, &waitInfo, UINT64_MAX);
130#endif
131 if (!map.success) {
132 std::fprintf(stderr, "[webgpu] texture readback map failed: status=%d done=%d\n", int(map.status),
133 map.done ? 1 : 0);
134 return false;
135 }
136
137 const uint8_t *data = static_cast<const uint8_t *>(dst.GetConstMappedRange(0, size));
138 if (!data) return false;
139 const size_t tightRowBytes = static_cast<size_t>(width) * bytesPerPixel;
140 outRgba.resize(tightRowBytes * height);
141 for (int y = 0; y < height; ++y)
142 std::memcpy(outRgba.data() + size_t(y) * tightRowBytes, data + size_t(y) * bytesPerRow, tightRowBytes);
143 dst.Unmap();
144 return true;
145}
146
147float decodeHalf(uint16_t value) {
148 const uint32_t sign = uint32_t(value & 0x8000u) << 16u;
149 uint32_t exponent = (value >> 10u) & 0x1fu;
150 uint32_t mantissa = value & 0x03ffu;
151 uint32_t bits = 0;
152 if (exponent == 0) {
153 if (mantissa == 0) {
154 bits = sign;
155 } else {
156 exponent = 1;
157 while ((mantissa & 0x0400u) == 0) {
158 mantissa <<= 1u;
159 --exponent;
160 }
161 mantissa &= 0x03ffu;
162 bits = sign | ((exponent + 112u) << 23u) | (mantissa << 13u);
163 }
164 } else if (exponent == 31u) {
165 bits = sign | 0x7f800000u | (mantissa << 13u);
166 } else {
167 bits = sign | ((exponent + 112u) << 23u) | (mantissa << 13u);
168 }
169 float result = 0.f;
170 std::memcpy(&result, &bits, sizeof(result));
171 return result;
172}
173
174float hdrToDisplay(float value) {
175 const float linear = std::max(value, 0.f);
176 const float mapped =
177 std::clamp((linear * (2.51f * linear + 0.03f)) / (linear * (2.43f * linear + 0.59f) + 0.14f), 0.f, 1.f);
178 return mapped <= 0.0031308f ? mapped * 12.92f : 1.055f * std::pow(mapped, 1.f / 2.4f) - 0.055f;
179}
180
181Color hdrPixelToColor(const uint8_t *pixel) {
182 uint16_t channels[4]{};
183 std::memcpy(channels, pixel, sizeof(channels));
184 return Color(hdrToDisplay(decodeHalf(channels[0])), hdrToDisplay(decodeHalf(channels[1])),
185 hdrToDisplay(decodeHalf(channels[2])), std::clamp(decodeHalf(channels[3]), 0.f, 1.f));
186}
187
188} // namespace
189
191 const bool presented = !canvas && screenReadbackEnabled && presentedReadback;
192 const bool bgra = presented && (presentedReadbackFormat == WGPUTextureFormat_BGRA8Unorm ||
193 presentedReadbackFormat == WGPUTextureFormat_BGRA8UnormSrgb);
194 int w = presented ? presentedReadbackW : canvas ? canvas->getWidth() : (sceneColorWidth > 0 ? sceneColorWidth : 1);
195 int h = presented ? presentedReadbackH
196 : canvas ? canvas->getHeight()
197 : (sceneColorHeight > 0 ? sceneColorHeight : 1);
198 if (x < 0 || y < 0 || x >= w || y >= h) return Color(0.f, 0.f, 0.f, 0.f);
199 std::vector<uint8_t> rgba;
200 wgpu::Texture src = presented ? presentedReadback
201 : canvas ? canvas->color
202 : (sceneColorSlots.empty() ? nullptr : sceneColorSlots[lastPresentSlot].color);
203 const bool hdrScene = !presented && canvas == nullptr && sceneColorFormat == WGPUTextureFormat_RGBA16Float;
204 if (!src || !copyTextureToCpu(instance, device, queue, src, w, h, rgba, hdrScene ? 8 : 4)) return clearColor;
205 if (hdrScene) return hdrPixelToColor(rgba.data() + (size_t(y) * w + x) * 8);
206 const uint8_t *p = rgba.data() + (size_t(y) * w + x) * 4;
207 return Color(p[bgra ? 2 : 0] / 255.f, p[1] / 255.f, p[bgra ? 0 : 2] / 255.f, p[3] / 255.f);
208}
209
211 const bool presented = !canvas && screenReadbackEnabled && presentedReadback;
212 const bool bgra = presented && (presentedReadbackFormat == WGPUTextureFormat_BGRA8Unorm ||
213 presentedReadbackFormat == WGPUTextureFormat_BGRA8UnormSrgb);
214 int w = presented ? presentedReadbackW : canvas ? canvas->getWidth() : (sceneColorWidth > 0 ? sceneColorWidth : 1);
215 int h = presented ? presentedReadbackH
216 : canvas ? canvas->getHeight()
217 : (sceneColorHeight > 0 ? sceneColorHeight : 1);
218 auto *img = new image::ImageData(w, h, "RGBA8");
219 std::vector<uint8_t> rgba;
220 wgpu::Texture src = presented ? presentedReadback
221 : canvas ? canvas->color
222 : (sceneColorSlots.empty() ? nullptr : sceneColorSlots[lastPresentSlot].color);
223 const bool hdrScene = !presented && canvas == nullptr && sceneColorFormat == WGPUTextureFormat_RGBA16Float;
224 if (src && copyTextureToCpu(instance, device, queue, src, w, h, rgba, hdrScene ? 8 : 4)) {
225 if (!hdrScene) {
226 if (bgra)
227 for (size_t pixel = 0; pixel < size_t(w) * h; ++pixel) std::swap(rgba[pixel * 4], rgba[pixel * 4 + 2]);
228 std::memcpy(img->getData(), rgba.data(), rgba.size());
229 } else {
230 auto *dst = static_cast<uint8_t *>(img->getData());
231 for (size_t pixel = 0; pixel < size_t(w) * h; ++pixel) {
232 const Color color = hdrPixelToColor(rgba.data() + pixel * 8);
233 dst[pixel * 4 + 0] = uint8_t(std::clamp(color.r * 255.f, 0.f, 255.f));
234 dst[pixel * 4 + 1] = uint8_t(std::clamp(color.g * 255.f, 0.f, 255.f));
235 dst[pixel * 4 + 2] = uint8_t(std::clamp(color.b * 255.f, 0.f, 255.f));
236 dst[pixel * 4 + 3] = uint8_t(std::clamp(color.a * 255.f, 0.f, 255.f));
237 }
238 }
239 }
240 return img;
241}
242
244 if (!canvas || !canvas->hdr) return nullptr;
245 const int w = canvas->getWidth();
246 const int h = canvas->getHeight();
247 std::vector<uint8_t> rgba16f;
248 if (!copyTextureToCpu(instance, device, queue, canvas->color, w, h, rgba16f, 8)) return nullptr;
249 auto *img = new image::ImageData(w, h, "RGBA16F");
250 std::memcpy(img->getData(), rgba16f.data(), rgba16f.size());
251 return img;
252}
253
254image::ImageData *Graphics::readGBufferToImageData(const std::string &attachment) {
255 submitPendingDeferredPasses();
256 if (!device || gbufferSlots.empty() || gbufferWidth <= 0 || gbufferHeight <= 0) return nullptr;
257 GbufferSlot &slot = gbufferSlots[std::min<size_t>(lastGbufferSlot, gbufferSlots.size() - 1)];
258 wgpu::Texture src;
259 if (attachment == "depth")
260 src = slot.depthColor;
261 else if (attachment == "normal")
262 src = slot.normal;
263 else if (attachment == "albedo")
264 src = slot.albedo;
265 else if (attachment == "pbrParams")
266 src = slot.pbrParams;
267 else if (attachment == "emissive")
268 src = slot.emissive;
269 else
270 return nullptr;
271
272 std::vector<uint8_t> rgba;
273 if (!copyTextureToCpu(instance, device, queue, src, gbufferWidth, gbufferHeight, rgba)) return nullptr;
274 auto *image = new image::ImageData(gbufferWidth, gbufferHeight, "RGBA8");
275 std::memcpy(image->getData(), rgba.data(), rgba.size());
276 if (attachment == "depth") {
277 // Mirror Vulkan readback: expose linear depth as grayscale (G/B hold velocity on GPU).
278 auto* pixels = static_cast<uint8_t*>(image->getData());
279 const size_t pixelCount = size_t(gbufferWidth) * size_t(gbufferHeight);
280 for (size_t i = 0; i < pixelCount; ++i) {
281 pixels[i * 4u + 1u] = pixels[i * 4u];
282 pixels[i * 4u + 2u] = pixels[i * 4u];
283 }
284 }
285 return image;
286}
287
289 submitPendingDeferredPasses();
290 if (!device || decalSlots.empty() || decalWidth <= 0 || decalHeight <= 0) return nullptr;
291 DecalSlot &slot = decalSlots[std::min<size_t>(lastDecalSlot, decalSlots.size() - 1)];
292 wgpu::Texture src;
293 if (attachment == "albedo")
294 src = slot.albedo;
295 else if (attachment == "normal")
296 src = slot.normal;
297 else if (attachment == "params")
298 src = slot.params;
299 else
300 return nullptr;
301 std::vector<uint8_t> rgba;
302 if (!copyTextureToCpu(instance, device, queue, src, decalWidth, decalHeight, rgba)) return nullptr;
303 auto *image = new image::ImageData(decalWidth, decalHeight, "RGBA8");
304 std::memcpy(image->getData(), rgba.data(), rgba.size());
305 return image;
306}
307
308// ---------------------------------------------------------------------------
309// Async frame readback (browser)
310// ---------------------------------------------------------------------------
311
312namespace {
313
314bool encodeRgbaToPng(const std::string &path, int width, int height, const std::vector<uint8_t> &rgba) {
315 try {
316 image::ImageData img(width, height, "RGBA8");
317 std::memcpy(img.getData(), rgba.data(), rgba.size());
318 std::unique_ptr<eve::filesystem::FileData> png(
319 img.encode(medialoader::FormatHandler::ENCODED_PNG, path.c_str(), false));
320 if (!png) return false;
321 std::ofstream out(path, std::ios::binary);
322 if (!out) return false;
323 out.write(static_cast<const char *>(png->getData()), static_cast<std::streamsize>(png->getSize()));
324 return out.good();
325 } catch (...) {
326 return false;
327 }
328}
329
330} // namespace
331
332bool Graphics::beginFrameReadback(const std::string &path) {
333 // A finished readback can be replaced; only refuse while one is in flight.
334 if ((pendingReadback_ && !pendingReadback_->done) || !device) return false;
335 const bool presented = screenReadbackEnabled && presentedReadback;
336 wgpu::Texture src = presented ? presentedReadback : lastReadbackTex;
337 int w = presented ? presentedReadbackW : lastReadbackW;
338 int h = presented ? presentedReadbackH : lastReadbackH;
339 if (!src || w <= 0 || h <= 0) {
340 // Fall back to the scene color slot before anything was rendered.
341 if (sceneColorSlots.empty()) return false;
342 const uint32_t slot = lastPresentSlot;
343 if (slot >= sceneColorSlots.size()) return false;
344 src = sceneColorSlots[slot].color;
345 w = sceneColorWidth;
346 h = sceneColorHeight;
347 if (!src || w <= 0 || h <= 0) return false;
348 }
349
350 const bool hdr = src.GetFormat() == wgpu::TextureFormat::RGBA16Float;
351 const bool bgra =
352 src.GetFormat() == wgpu::TextureFormat::BGRA8Unorm || src.GetFormat() == wgpu::TextureFormat::BGRA8UnormSrgb;
353 uint64_t bytesPerRow = static_cast<uint64_t>(w * (hdr ? 8 : 4));
354 bytesPerRow = (bytesPerRow + 255) / 256 * 256;
355 const uint64_t size = bytesPerRow * static_cast<uint64_t>(h);
356
357 WGPUBufferDescriptor bd{};
358 bd.label = sv("eve_readback");
359 bd.size = size;
360 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead;
361 bd.mappedAtCreation = false;
362 wgpu::Buffer dst = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor *>(&bd));
363 if (!dst) return false;
364
365 // Copy the resolved scene color into the readback buffer, then map
366 // asynchronously (two-phase: the pump waits for the map callback on the
367 // browser event loop without ASYNCIFY sleeps).
368 wgpu::CommandEncoder enc = device.CreateCommandEncoder();
369 WGPUTexelCopyTextureInfo from{};
370 from.texture = src.Get();
371 from.mipLevel = 0;
372 from.aspect = WGPUTextureAspect_All;
373 from.origin = {0, 0, 0};
374 WGPUTexelCopyBufferInfo to{};
375 to.buffer = dst.Get();
376 to.layout.offset = 0;
377 to.layout.bytesPerRow = static_cast<uint32_t>(bytesPerRow);
378 to.layout.rowsPerImage = static_cast<uint32_t>(h);
379 WGPUExtent3D extent{static_cast<uint32_t>(w), static_cast<uint32_t>(h), 1};
380 enc.CopyTextureToBuffer(reinterpret_cast<const wgpu::TexelCopyTextureInfo *>(&from),
381 reinterpret_cast<const wgpu::TexelCopyBufferInfo *>(&to),
382 reinterpret_cast<const wgpu::Extent3D *>(&extent));
383 wgpu::CommandBuffer cmd = enc.Finish();
384 queue.Submit(1, &cmd);
385
386 auto pr = std::make_unique<PendingReadback>();
387 pr->path = path;
388 pr->width = w;
389 pr->height = h;
390 pr->bytesPerRow = bytesPerRow;
391 pr->dst = dst;
392 pr->hdr = hdr;
393 pr->bgra = bgra;
394
395 WGPUBufferMapCallbackInfo cbInfo{};
396 cbInfo.mode = WGPUCallbackMode_AllowProcessEvents;
397 cbInfo.callback = [](WGPUMapAsyncStatus status, WGPUStringView /*message*/, void *userdata1, void * /*userdata2*/) {
398 auto *p = static_cast<PendingReadback *>(userdata1);
399 p->mapped = (status == WGPUMapAsyncStatus_Success);
400 };
401 cbInfo.userdata1 = pr.get();
402 wgpuBufferMapAsync(dst.Get(), WGPUMapMode_Read, 0, size, cbInfo);
403
404 pendingReadback_ = std::move(pr);
405 return true;
406}
407
409 if (!pendingReadback_) return 0;
410 if (!pendingReadback_->done) return 1;
411 return pendingReadback_->ok ? 2 : 3;
412}
413
415 if (!pendingReadback_ || pendingReadback_->done) return;
416 auto &pr = *pendingReadback_;
417 if (!pr.mapped) {
418 // The map callback is delivered on the browser event loop between
419 // frames (emdawnwebgpu callUserCallback), so no ASYNCIFY sleep is
420 // needed here �?this runs from present() on the main loop.
421#if defined(__EMSCRIPTEN__)
422 wgpuInstanceProcessEvents(instance.Get());
423#endif
424 return;
425 }
426 const uint8_t *data =
427 static_cast<const uint8_t *>(pr.dst.GetConstMappedRange(0, pr.bytesPerRow * static_cast<uint64_t>(pr.height)));
428 std::vector<uint8_t> rgba(static_cast<size_t>(pr.width) * pr.height * 4);
429 if (data) {
430 for (int y = 0; y < pr.height; ++y) {
431 auto *dst = rgba.data() + size_t(y) * pr.width * 4;
432 const auto *row = data + size_t(y) * pr.bytesPerRow;
433 for (int x = 0; x < pr.width; ++x) {
434 if (pr.hdr) {
435 const auto color = hdrPixelToColor(row + size_t(x) * 8);
436 dst[x * 4] = uint8_t(std::clamp(color.r * 255.f, 0.f, 255.f));
437 dst[x * 4 + 1] = uint8_t(std::clamp(color.g * 255.f, 0.f, 255.f));
438 dst[x * 4 + 2] = uint8_t(std::clamp(color.b * 255.f, 0.f, 255.f));
439 dst[x * 4 + 3] = uint8_t(std::clamp(color.a * 255.f, 0.f, 255.f));
440 } else {
441 dst[x * 4] = row[x * 4 + (pr.bgra ? 2 : 0)];
442 dst[x * 4 + 1] = row[x * 4 + 1];
443 dst[x * 4 + 2] = row[x * 4 + (pr.bgra ? 0 : 2)];
444 dst[x * 4 + 3] = row[x * 4 + 3];
445 }
446 }
447 }
448 }
449 pr.dst.Unmap();
450 pr.ok = data && encodeRgbaToPng(pr.path, pr.width, pr.height, rgba);
451 pr.done = true;
452}
453
454
455} // namespace eve::graphics::webgpu
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
std::map< std::string, std::vector< Key >, std::less<> > channels
std::string from
glm::vec4 p[6]
scene::NodeDesc desc
vkb::Device & device
vk::UniqueImage image
wgpu::PopErrorScopeStatus status
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
int h
std::uint32_t height
std::uint32_t width
std::string path
Definition PlayHost.cpp:110
uint8_t * pixels
float size
Definition TreeMesh.cpp:156
image::ImageData * readGBufferToImageData(const std::string &attachment) override
Reads g buffer to image data.
void pumpReadback()
Advances a pending frame readback; called every present().
Color getPixelImpl(OffscreenCanvas *canvas, int x, int y)
Blocking CPU readback of an offscreen canvas or scene color target.
image::ImageData * newImageData() override
Creates a image data. @ownership Caller deletes unless documented otherwise.
image::ImageData * newHDRImageDataImpl(OffscreenCanvas *canvas)
Blocking linear RGBA16F readback of an HDR offscreen canvas. @ownership The caller owns the returned ...
bool beginFrameReadback(const std::string &path) override
Begins frame readback.
image::ImageData * newImageDataImpl(OffscreenCanvas *canvas)
Creates a image data impl. @ownership Caller deletes unless documented otherwise.
int frameReadbackStatus() const override
Frame readback status.
Color getPixel(int x, int y) override
Returns the pixel.
image::ImageData * readDecalLayerToImageData(const std::string &attachment) override
Reads decal layer to image data.
Offscreen render target (RGBA8Unorm color, optional Depth32Float). 2D batches are flushed into the ca...
Definition Canvas.h:16
int getWidth() const override
Returns the width.
Definition Canvas.h:24
int getHeight() const override
Returns the height.
Definition Canvas.h:26
Represents raw pixel data.
Definition ImageData.h:38
WGPUStringView sv(const char *s)
Build a WGPUStringView from a C string (null-safe, length auto-computed).
Definition wgpu_types.h:20
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
std::future< vkb::Instance > future
Definition Graphics.cpp:182
glm::vec4 color