Skip to content

Commit 4494ab1

Browse files
authored
Implement view_formats in create_texture (#832)
`create_texture()` accepted a `view_formats` argument and then raised NotImplementedError for any non-empty value, while the descriptor it fills already carried `viewFormatCount` / `viewFormats`, commented out as unused. This wires them up using the same conversion the surface configuration path in this file already performs. WebGPU allows a texture view to reinterpret the texture as any format listed in `viewFormats` at creation, and only those. The common use is the srgb/non-srgb pair: sampling an srgb texture applies the transfer function, so reading back the bytes that were actually written requires a view in the plain format. Without `view_formats` there is no way to ask for that view, and the only workaround is to copy the whole texture into a second one of the plain format -- a full-frame copy, every frame, plus the memory for the duplicate. Adds a test covering a declared view format, the texture's own format, an undeclared format still being rejected, and a texture that declares none keeping the previous behaviour.
1 parent 2c6ce9a commit 4494ab1

2 files changed

Lines changed: 39 additions & 6 deletions

File tree

tests/test_wgpu_native_texture.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,5 +324,37 @@ def test_16bit_norm():
324324
assert "r16unorm" in texture.format
325325

326326

327+
@mark.skipif(not can_use_wgpu_lib, reason="Needs wgpu lib")
328+
def test_view_formats():
329+
# A view may reinterpret a texture as any format declared in view_formats,
330+
# and only those. The srgb/non-srgb pair is the useful case: it reads the
331+
# bytes that were written, without the transfer function.
332+
device = wgpu.utils.get_default_device()
333+
334+
usage = wgpu.TextureUsage.RENDER_ATTACHMENT | wgpu.TextureUsage.TEXTURE_BINDING
335+
tex = device.create_texture(
336+
size=(64, 64, 1),
337+
format=wgpu.TextureFormat.rgba8unorm_srgb,
338+
usage=usage,
339+
view_formats=[wgpu.TextureFormat.rgba8unorm],
340+
)
341+
view = tex.create_view(format=wgpu.TextureFormat.rgba8unorm)
342+
assert view is not None
343+
344+
# The texture's own format is always viewable.
345+
assert tex.create_view(format=wgpu.TextureFormat.rgba8unorm_srgb) is not None
346+
347+
# A format that was not declared is still rejected.
348+
with raises(wgpu.GPUValidationError):
349+
tex.create_view(format=wgpu.TextureFormat.rgba8snorm)
350+
351+
# And declaring none keeps the old behaviour.
352+
plain = device.create_texture(
353+
size=(64, 64, 1), format=wgpu.TextureFormat.rgba8unorm_srgb, usage=usage
354+
)
355+
with raises(wgpu.GPUValidationError):
356+
plain.create_view(format=wgpu.TextureFormat.rgba8unorm)
357+
358+
327359
if __name__ == "__main__":
328360
run_tests(globals())

wgpu/backends/wgpu_native/_api.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,10 +1498,11 @@ def create_texture(
14981498
depthOrArrayLayers=size[2],
14991499
)
15001500

1501-
if view_formats:
1502-
raise NotImplementedError(
1503-
"create_texture(.. view_formats is not yet supported."
1504-
)
1501+
# Formats that views of this texture may reinterpret it as. The main
1502+
# use is taking a non-srgb view of an srgb target (or the reverse) to
1503+
# read or write the raw bytes without the transfer function applied.
1504+
view_formats_list = [enummap["TextureFormat." + x] for x in view_formats]
1505+
c_view_formats = new_array("WGPUTextureFormat[]", view_formats_list)
15051506

15061507
if not mip_level_count:
15071508
mip_level_count = 1 # or lib.WGPU_MIP_LEVEL_COUNT_UNDEFINED ?
@@ -1522,8 +1523,8 @@ def create_texture(
15221523
dimension=dimension,
15231524
format=format,
15241525
usage=usage,
1525-
# not used: viewFormatCount
1526-
# not used: viewFormats
1526+
viewFormatCount=len(view_formats),
1527+
viewFormats=c_view_formats,
15271528
)
15281529
# H: WGPUTexture f(WGPUDevice device, WGPUTextureDescriptor const * descriptor)
15291530
id = libf.wgpuDeviceCreateTexture(self._internal, struct)

0 commit comments

Comments
 (0)