载入中...
搜索中...
未找到
GraphicsTextureMips.cpp
浏览该文件的文档.
2
3#if !defined(__EMSCRIPTEN__)
4#include <algorithm>
5#include <array>
6#include <chrono>
7#include <cstring>
8#include <limits>
9
10namespace eve::graphics::webgpu {
11namespace {
12struct UploadError {
13 wgpu::PopErrorScopeStatus status = wgpu::PopErrorScopeStatus::Error;
14 wgpu::ErrorType type = wgpu::ErrorType::NoError;
15 std::array<char, 512> message{};
16};
17
18// Scopes belong to this graphics-thread operation. Only its own callbacks are
19// dispatched by WaitAny; callback state outlives a failed/timed-out observation.
20class UploadScopes {
21public:
22 explicit UploadScopes(const wgpu::Device& device) : device_(device) {
23 for (auto& result : results_) result = std::make_shared<UploadError>();
24 for (auto filter :
25 {wgpu::ErrorFilter::Validation, wgpu::ErrorFilter::OutOfMemory, wgpu::ErrorFilter::Internal}) {
26 device_.PushErrorScope(filter);
27 ++pending_;
28 }
29 }
30 ~UploadScopes() {
31 // Unwind any scopes left by a CPU allocation exception before publication.
32 while (pending_) {
33 WGPUPopErrorScopeCallbackInfo callback{};
34 callback.mode = WGPUCallbackMode_AllowSpontaneous;
35 callback.callback = [](WGPUPopErrorScopeStatus, WGPUErrorType, WGPUStringView, void*, void*) {};
36 (void)wgpuDevicePopErrorScope(device_.Get(), callback);
37 --pending_;
38 }
39 }
40 [[nodiscard]] Result<void> finish(const wgpu::Instance& instance) {
41 std::array<wgpu::Future, 3> futures{};
42 for (size_t i = 0; i < futures.size(); ++i) {
43 futures[i] = device_.PopErrorScope(
44 wgpu::CallbackMode::WaitAnyOnly,
45 [result = results_[i]](wgpu::PopErrorScopeStatus status, wgpu::ErrorType type,
46 wgpu::StringView message) noexcept {
47 result->status = status;
48 result->type = type;
49 if (message.data) {
50 const auto length = message.length == WGPU_STRLEN ? std::strlen(message.data) : message.length;
51 std::memcpy(result->message.data(), message.data, std::min(length, result->message.size() - 1));
52 }
53 });
54 --pending_;
55 }
56 const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(30);
57 for (auto future : futures) {
58 const auto remaining = deadline - std::chrono::steady_clock::now();
59 const auto timeout =
60 std::max<int64_t>(0, std::chrono::duration_cast<std::chrono::nanoseconds>(remaining).count());
61 if (instance.WaitAny(future, uint64_t(timeout)) != wgpu::WaitStatus::Success)
63 "WebGPU mip upload error scope could not be observed",
64 {}, {}, "graphics.texture.mips"));
65 }
66 for (const auto& result : results_)
67 if (result->status != wgpu::PopErrorScopeStatus::Success || result->type != wgpu::ErrorType::NoError)
68 return Result<void>::failure(Diagnostic::error(
69 DiagnosticCode::Failed, std::string("WebGPU mip upload rejected: ") + result->message.data(), {},
70 {}, "graphics.texture.mips"));
71 return Result<void>::success();
72 }
73
74private:
75 wgpu::Device device_;
76 unsigned pending_ = 0;
77 std::array<std::shared_ptr<UploadError>, 3> results_;
78};
79} // namespace
80
81Result<Texture*> Graphics::newTextureMipChain(uint32_t width, uint32_t height, uint32_t levels,
82 std::span<const uint8_t> rgba) {
83 auto fail = [](DiagnosticCode code, std::string message) {
84 return Result<Texture*>::failure(Diagnostic::error(code, message, {}, {}, "graphics.texture.mips"));
85 };
86 if (!initialized) return fail(DiagnosticCode::Failed, "graphics is not initialized");
87 const auto maximum = caps.maxTextureDimension2D();
88 if (!width || !height || width > maximum || height > maximum)
89 return fail(DiagnosticCode::InvalidArgument, "mip dimensions exceed device limits");
90 uint64_t expected = 0;
91 uint32_t count = 0, w = width, h = height;
92 for (;;) {
93 expected += uint64_t(w) * h * 4;
94 ++count;
95 if (w == 1 && h == 1) break;
96 w = std::max(w / 2, 1u);
97 h = std::max(h / 2, 1u);
98 }
99 if (levels != count || expected != rgba.size() || expected > UINT32_MAX)
100 return fail(DiagnosticCode::InvalidArgument, "mip count, packed bytes or staging offset budget invalid");
101 try {
102 ownedTextures.reserve(ownedTextures.size() + 1);
103 ownedGpuTextures.reserve(ownedGpuTextures.size() + 1);
104 auto tex = std::make_unique<Texture>();
105 auto gpu = std::make_unique<GpuTexture>();
106 gpu->width = int(width);
107 gpu->height = int(height);
108 gpu->mipLevels = levels;
109 gpu->samplerState = TextureSampler::linearMipmap();
110 UploadScopes scopes(device);
111 wgpu::TextureDescriptor descriptor{};
112 descriptor.dimension = wgpu::TextureDimension::e2D;
113 descriptor.size = {width, height, 1};
114 descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
115 descriptor.mipLevelCount = levels;
116 descriptor.sampleCount = 1;
117 descriptor.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst;
118 gpu->texture = device.CreateTexture(&descriptor);
119 if (!gpu->texture) {
120 auto checked = scopes.finish(instance);
121 if (!checked) return fail(DiagnosticCode::Failed, checked.error()->message());
122 return fail(DiagnosticCode::Failed, "WebGPU mip texture allocation returned no object");
123 }
124 w = width;
125 h = height;
126 size_t offset = 0;
127 for (uint32_t level = 0; level < levels; ++level) {
128 wgpu::TexelCopyTextureInfo destination{};
129 destination.texture = gpu->texture;
130 destination.mipLevel = level;
131 wgpu::TexelCopyBufferLayout layout{};
132 layout.bytesPerRow = w * 4;
133 layout.rowsPerImage = h;
134 wgpu::Extent3D extent{w, h, 1};
135 const size_t bytes = size_t(w) * h * 4;
136 queue.WriteTexture(&destination, rgba.data() + offset, bytes, &layout, &extent);
137 offset += bytes;
138 w = std::max(w / 2, 1u);
139 h = std::max(h / 2, 1u);
140 }
141 gpu->view = gpu->texture.CreateView();
142 gpu->sampler = makeSampler(gpu->samplerState, levels);
143 auto checked = scopes.finish(instance);
144 if (!checked) return fail(DiagnosticCode::Failed, checked.error()->message());
145 if (!gpu->view || !gpu->sampler)
146 return fail(DiagnosticCode::Failed, "WebGPU mip view or sampler allocation returned no object");
147 tex->width = tex->pixelWidth = int(width);
148 tex->height = tex->pixelHeight = int(height);
149 tex->mipmapCount = int(levels);
150 tex->sampler = gpu->samplerState;
151 tex->gpuHandle = gpu.get();
152 auto* result = tex.get();
153 ownedTextures.push_back(std::move(tex));
154 ownedGpuTextures.push_back(std::move(gpu));
155 return Result<Texture*>::success(result);
156 } catch (const std::exception& error) {
157 return fail(DiagnosticCode::Failed, error.what());
158 }
159}
160
162 std::span<const uint16_t> rgbaHalf) {
163 auto fail = [](DiagnosticCode code, std::string message) {
165 Diagnostic::error(code, message, {}, {}, "graphics.texture.array"));
166 };
167 if (!initialized) return fail(DiagnosticCode::Failed, "graphics is not initialized");
168 wgpu::Limits limits{};
169 const uint64_t texels = uint64_t(width) * height * layers;
170 if (device.GetLimits(&limits) != wgpu::Status::Success || !width || !height || !layers ||
171 width > limits.maxTextureDimension2D || height > limits.maxTextureDimension2D ||
172 layers > limits.maxTextureArrayLayers || texels > SIZE_MAX / 4u || rgbaHalf.size() != texels * 4u)
173 return fail(DiagnosticCode::InvalidArgument, "array dimensions or packed RGBA16F size are invalid");
174 try {
175 ownedTextures.reserve(ownedTextures.size() + 1u);
176 ownedGpuTextures.reserve(ownedGpuTextures.size() + 1u);
177 auto tex = std::make_unique<Texture>();
178 auto gpu = std::make_unique<GpuTexture>();
179 gpu->width = int(width);
180 gpu->height = int(height);
181 gpu->isArray = true;
182 gpu->samplerState = TextureSampler::linear();
183 UploadScopes scopes(device);
184 wgpu::TextureDescriptor descriptor{};
185 descriptor.dimension = wgpu::TextureDimension::e2D;
186 descriptor.size = {width, height, layers};
187 descriptor.format = wgpu::TextureFormat::RGBA16Float;
188 descriptor.mipLevelCount = 1;
189 descriptor.sampleCount = 1;
190 descriptor.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst;
191 gpu->texture = device.CreateTexture(&descriptor);
192 wgpu::TexelCopyTextureInfo destination{};
193 destination.texture = gpu->texture;
194 wgpu::TexelCopyBufferLayout layout{};
195 layout.bytesPerRow = width * 8u;
196 layout.rowsPerImage = height;
197 const wgpu::Extent3D extent{width, height, layers};
198 queue.WriteTexture(&destination, rgbaHalf.data(), rgbaHalf.size_bytes(), &layout, &extent);
199 wgpu::TextureViewDescriptor view{};
200 view.format = wgpu::TextureFormat::RGBA16Float;
201 view.dimension = wgpu::TextureViewDimension::e2DArray;
202 view.mipLevelCount = 1;
203 view.arrayLayerCount = layers;
204 gpu->view = gpu->texture.CreateView(&view);
205 gpu->sampler = makeSampler(gpu->samplerState, 1);
206 auto checked = scopes.finish(instance);
207 if (!checked) return fail(DiagnosticCode::Failed, checked.error()->message());
208 if (!gpu->texture || !gpu->view || !gpu->sampler)
209 return fail(DiagnosticCode::Failed, "WebGPU array texture allocation returned no object");
210 tex->width = tex->pixelWidth = int(width);
211 tex->height = tex->pixelHeight = int(height);
212 tex->layers = int(layers);
213 tex->sampler = gpu->samplerState;
214 tex->gpuHandle = gpu.get();
215 auto* result = tex.get();
216 ownedTextures.push_back(std::move(tex));
217 ownedGpuTextures.push_back(std::move(gpu));
218 return Result<Texture*>::success(result);
219 } catch (const std::exception& error) {
220 return fail(DiagnosticCode::Failed, error.what());
221 }
222}
223
225 std::span<const uint8_t> rgba) {
226 auto fail = [](DiagnosticCode code, std::string message) {
228 Diagnostic::error(code, message, {}, {}, "graphics.texture.volume"));
229 };
230 if (!initialized) return fail(DiagnosticCode::Failed, "graphics is not initialized");
231 wgpu::Limits limits{};
232 const uint64_t texels = uint64_t(width) * height * depth;
233 if (device.GetLimits(&limits) != wgpu::Status::Success || !width || !height || !depth ||
234 width > limits.maxTextureDimension3D || height > limits.maxTextureDimension3D ||
235 depth > limits.maxTextureDimension3D || texels > SIZE_MAX / 4u || rgba.size() != texels * 4u)
236 return fail(DiagnosticCode::InvalidArgument, "volume dimensions or packed RGBA8 size are invalid");
237 try {
238 ownedTextures.reserve(ownedTextures.size() + 1u);
239 ownedGpuTextures.reserve(ownedGpuTextures.size() + 1u);
240 auto tex = std::make_unique<Texture>();
241 auto gpu = std::make_unique<GpuTexture>();
242 gpu->width = int(width);
243 gpu->height = int(height);
244 gpu->isVolume = true;
245 gpu->samplerState = TextureSampler::linear();
246 gpu->samplerState.repeatU = gpu->samplerState.repeatV = gpu->samplerState.repeatW = true;
247 UploadScopes scopes(device);
248 wgpu::TextureDescriptor descriptor{};
249 descriptor.dimension = wgpu::TextureDimension::e3D;
250 descriptor.size = {width, height, depth};
251 descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
252 descriptor.mipLevelCount = 1;
253 descriptor.sampleCount = 1;
254 descriptor.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst;
255 gpu->texture = device.CreateTexture(&descriptor);
256 wgpu::TexelCopyTextureInfo destination{};
257 destination.texture = gpu->texture;
258 wgpu::TexelCopyBufferLayout layout{};
259 layout.bytesPerRow = width * 4u;
260 layout.rowsPerImage = height;
261 const wgpu::Extent3D extent{width, height, depth};
262 queue.WriteTexture(&destination, rgba.data(), rgba.size(), &layout, &extent);
263 wgpu::TextureViewDescriptor view{};
264 view.format = wgpu::TextureFormat::RGBA8Unorm;
265 view.dimension = wgpu::TextureViewDimension::e3D;
266 view.mipLevelCount = 1;
267 view.arrayLayerCount = 1;
268 gpu->view = gpu->texture.CreateView(&view);
269 gpu->sampler = makeSampler(gpu->samplerState, 1);
270 auto checked = scopes.finish(instance);
271 if (!checked) return fail(DiagnosticCode::Failed, checked.error()->message());
272 if (!gpu->texture || !gpu->view || !gpu->sampler)
273 return fail(DiagnosticCode::Failed, "WebGPU volume texture allocation returned no object");
274 tex->width = tex->pixelWidth = int(width);
275 tex->height = tex->pixelHeight = int(height);
276 tex->depth = int(depth);
277 tex->sampler = gpu->samplerState;
278 tex->gpuHandle = gpu.get();
279 auto* result = tex.get();
280 ownedTextures.push_back(std::move(tex));
281 ownedGpuTextures.push_back(std::move(gpu));
282 return Result<Texture*>::success(result);
283 } catch (const std::exception& error) {
284 return fail(DiagnosticCode::Failed, error.what());
285 }
286}
287} // namespace eve::graphics::webgpu
288#endif
float w
Definition AnimClip.cpp:738
std::string descriptor
std::string layout
std::string message
DiagnosticCode code
float maximum[3]
vkb::Device & device
wgpu::PopErrorScopeStatus status
int h
std::uint32_t height
std::uint32_t width
size_t offset
std::uint64_t bytes
int level
std::string error
Definition Package.cpp:60
Duration deadline
glm::mat4 view
std::string filter
std::string string
std::uint32_t count
std::uint32_t depth
const AssetImportLimits & limits
ViewPreparation callback
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
uint32_t maxTextureDimension2D() const
Maximum 2D texture dimension the device guarantees.
Result< Texture * > newTextureArrayRgba16f(uint32_t width, uint32_t height, uint32_t layers, std::span< const uint16_t > rgbaHalf) override
Upload a sampled 2D array of tightly packed linear RGBA16F texels.
Result< Texture * > newTexture3DRgba8(uint32_t width, uint32_t height, uint32_t depth, std::span< const uint8_t > rgba) override
Upload one tightly packed linear RGBA8 3D sampled texture.
Result< Texture * > newTextureMipChain(uint32_t width, uint32_t height, uint32_t levels, std::span< const uint8_t > rgba) override
Upload complete explicit linear RGBA8 mips without regenerating pixels.
eve::Diagnostic Diagnostic
eve::Result< T > Result
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
static TextureSampler linear()
Linear.
static TextureSampler linearMipmap()
Trilinear (linear + linear mips). Caller should create the texture with generateMipmaps.
std::future< vkb::Instance > future
Definition Graphics.cpp:182