Skip to content

Commit

Permalink
Make depth_stencil_attachment follow the spec (#611)
Browse files Browse the repository at this point in the history
* Make depth_stencil_attachment follow the spec

* Use wgpu.logger.warning()
Simplify test_wgpu_native_render.py
Add test that warnings occur, but only once.

* Was some confusion, and some objects had a Device in their _device field while others had a DeviceImpl.  Made it uniformly a Device.  Added test to ensure this was the case.

Fixed some typos.

* Simplify.  Every subclass of GPUObjectBase has a device in its _device field.

* Minor typos.

* Change requested by @almarklein

* Revert change
  • Loading branch information
fyellin authored Oct 8, 2024
1 parent 1158594 commit 68f2846
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 171 deletions.
2 changes: 1 addition & 1 deletion tests/test_wgpu_native_query_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_query_set():
)
assert query_set.count == query_count
assert query_set.type == query_type
assert query_set._device == device._internal
assert query_set._device == device
assert query_set.label == query_label

query_buf_size = 8 * query_set.count
Expand Down
194 changes: 82 additions & 112 deletions tests/test_wgpu_native_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import ctypes

import numpy as np
import sys

Expand All @@ -21,7 +22,7 @@
skip("These tests fail on dx12 for some reason", allow_module_level=True)


default_vertex_shader = """
DEFAULT_SHADER = """
@vertex
fn vs_main(@builtin(vertex_index) vertex_index : u32) -> @builtin(position) vec4<f32> {
var positions: array<vec3<f32>, 4> = array<vec3<f32>, 4>(
Expand All @@ -33,8 +34,18 @@
let p: vec3<f32> = positions[vertex_index];
return vec4<f32>(p, 1.0);
}
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""

DEPTH_STENCIL_TEX_FORMATS = [
wgpu.TextureFormat.depth16unorm,
wgpu.TextureFormat.depth24plus_stencil8,
wgpu.TextureFormat.depth32float,
]

# %% Simple square

Expand All @@ -48,20 +59,12 @@ def test_render_orange_square(use_render_bundle):
# NOTE: the 0.499 instead of 0.5 is to make sure the resulting value is 127.
# With 0.5 some drivers would produce 127 and others 128.

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

# Bindings and layout
bind_group = None
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args)
a = render_to_texture(
*render_args, size=(64, 64), use_render_bundle=use_render_bundle
Expand Down Expand Up @@ -89,14 +92,6 @@ def test_render_orange_square_indexed(use_render_bundle):

device = get_default_device()

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

# Bindings and layout
bind_group = None
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])
Expand All @@ -109,7 +104,7 @@ def test_render_orange_square_indexed(use_render_bundle):
)

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args, topology=wgpu.PrimitiveTopology.triangle_list, ibo=ibo)
a = render_to_texture(
*render_args,
Expand Down Expand Up @@ -138,14 +133,6 @@ def test_render_orange_square_indirect(use_render_bundle):

device = get_default_device()

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

# Bindings and layout
bind_group = None
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])
Expand All @@ -158,7 +145,7 @@ def test_render_orange_square_indirect(use_render_bundle):
)

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args, indirect_buffer=indirect_buffer)
a = render_to_texture(
*render_args,
Expand Down Expand Up @@ -186,14 +173,6 @@ def test_render_orange_square_indexed_indirect(use_render_bundle):

device = get_default_device()

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

# Bindings and layout
bind_group = None
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])
Expand All @@ -213,7 +192,7 @@ def test_render_orange_square_indexed_indirect(use_render_bundle):
)

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args, topology=wgpu.PrimitiveTopology.triangle_list, ibo=ibo, indirect_buffer=indirect_buffer)
a = render_to_texture(
*render_args,
Expand Down Expand Up @@ -309,14 +288,6 @@ def test_render_orange_square_color_attachment1(use_render_bundle):

device = get_default_device()

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

# Bindings and layout
bind_group = None
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])
Expand All @@ -329,7 +300,7 @@ def test_render_orange_square_color_attachment1(use_render_bundle):
}

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args, color_attachment=ca)
a = render_to_texture(
*render_args,
Expand Down Expand Up @@ -360,14 +331,6 @@ def test_render_orange_square_color_attachment2(use_render_bundle):

device = get_default_device()

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

# Bindings and layout
bind_group = None
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])
Expand All @@ -379,7 +342,7 @@ def test_render_orange_square_color_attachment2(use_render_bundle):
}

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args, color_attachment=ca)
a = render_to_texture(
*render_args,
Expand Down Expand Up @@ -411,14 +374,6 @@ def test_render_orange_square_viewport(use_render_bundle):

device = get_default_device()

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

def cb(renderpass):
renderpass.set_viewport(10, 20, 32, 32, 0, 1)

Expand All @@ -427,7 +382,7 @@ def cb(renderpass):
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args, renderpass_callback=cb)
a = render_to_texture(
*render_args,
Expand Down Expand Up @@ -455,14 +410,6 @@ def test_render_orange_square_scissor(use_render_bundle):

device = get_default_device()

fragment_shader = """
@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.499, 0.0, 1.0);
}
"""
shader_source = default_vertex_shader + fragment_shader

def cb(renderpass):
renderpass.set_scissor_rect(0, 0, 32, 32)
# Else set blend color. Does not change output, but covers the call.
Expand All @@ -473,7 +420,7 @@ def cb(renderpass):
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])

# Render
render_args = device, shader_source, pipeline_layout, bind_group
render_args = device, DEFAULT_SHADER, pipeline_layout, bind_group
# render_to_screen(*render_args, renderpass_callback=cb)
a = render_to_texture(
*render_args,
Expand All @@ -496,26 +443,9 @@ def cb(renderpass):


@pytest.mark.parametrize("use_render_bundle", [True, False])
def test_render_orange_square_depth16unorm(use_render_bundle):
@pytest.mark.parametrize("depth_stencil_tex_format", DEPTH_STENCIL_TEX_FORMATS)
def test_render_orange_square_with_depth(depth_stencil_tex_format, use_render_bundle):
"""Render an orange square, but disable half of it using a depth test using 16 bits."""
_render_orange_square_depth(wgpu.TextureFormat.depth16unorm, use_render_bundle)


@pytest.mark.parametrize("use_render_bundle", [True, False])
def test_render_orange_square_depth24plus_stencil8(use_render_bundle):
"""Render an orange square, but disable half of it using a depth test using 24 bits."""
_render_orange_square_depth(
wgpu.TextureFormat.depth24plus_stencil8, use_render_bundle
)


@pytest.mark.parametrize("use_render_bundle", [True, False])
def test_render_orange_square_depth32float(use_render_bundle):
"""Render an orange square, but disable half of it using a depth test using 32 bits."""
_render_orange_square_depth(wgpu.TextureFormat.depth32float, use_render_bundle)


def _render_orange_square_depth(depth_stencil_tex_format, use_render_bundle):
device = get_default_device()

shader_source = """
Expand Down Expand Up @@ -544,7 +474,7 @@ def cb(renderpass):
bind_group = None
pipeline_layout = device.create_pipeline_layout(bind_group_layouts=[])

# Create dept-stencil texture
# Create depth-stencil texture
depth_stencil_texture = device.create_texture(
size=(64, 64, 1), # when rendering to texture
# size=(640, 480, 1), # when rendering to screen
Expand All @@ -557,34 +487,20 @@ def cb(renderpass):
format=depth_stencil_tex_format,
depth_write_enabled=True,
depth_compare=wgpu.CompareFunction.less_equal,
# stencil_front={
# "compare": wgpu.CompareFunction.equal,
# "fail_op": wgpu.StencilOperation.keep,
# "depth_fail_op": wgpu.StencilOperation.keep,
# "pass_op": wgpu.StencilOperation.keep,
# },
# stencil_back={
# "compare": wgpu.CompareFunction.equal,
# "fail_op": wgpu.StencilOperation.keep,
# "depth_fail_op": wgpu.StencilOperation.keep,
# "pass_op": wgpu.StencilOperation.keep,
# },
stencil_read_mask=0,
stencil_write_mask=0,
depth_bias=0,
depth_bias_slope_scale=0.0,
depth_bias_clamp=0.0,
)

depth_stencil_attachment = dict(
view=depth_stencil_texture.create_view(),
depth_clear_value=0.1,
depth_load_op=wgpu.LoadOp.clear,
depth_store_op=wgpu.StoreOp.store,
stencil_load_op=wgpu.LoadOp.load,
stencil_store_op=wgpu.StoreOp.store,
)

if "stencil" in depth_stencil_tex_format:
depth_stencil_attachment["stencil_load_op"] = wgpu.LoadOp.load
depth_stencil_attachment["stencil_store_op"] = wgpu.StoreOp.store
# The default values of depth_stencil_state are fine.

# Render
render_args = device, shader_source, pipeline_layout, bind_group
# render_to_screen(*render_args, renderpass_callback=cb, depth_stencil_state=depth_stencil_state, depth_stencil_attachment=depth_stencil_attachment)
Expand Down Expand Up @@ -681,5 +597,59 @@ def test_render_orange_dots(use_render_bundle):
assert np.all(dot[:, :, 3] == 255) # alpha


@pytest.mark.parametrize("depth_stencil_tex_format", DEPTH_STENCIL_TEX_FORMATS)
def test_stencil_depth_warning(depth_stencil_tex_format, monkeypatch):
# Verify that when you use stencil_load_op or stencil_store_op, and there is
# no stencil, you'll get a warning. But only once.

# The only way we can see warnings is by monkeypatching wgpu.logger.warning
warnings = []
monkeypatch.setattr(wgpu.logger, "warning", lambda msg: warnings.append(msg))

# We need a new device for each run, since each device keeps track of which warnings
# it's issued.
adapter = wgpu.gpu.request_adapter_sync(power_preference="high-performance")
device = adapter.request_device_sync()

# Create depth-stencil texture
depth_stencil_texture = device.create_texture(
size=(64, 64, 1), # when rendering to texture
format=depth_stencil_tex_format,
usage=wgpu.TextureUsage.RENDER_ATTACHMENT,
)

depth_stencil_attachment = dict(
view=depth_stencil_texture.create_view(),
depth_clear_value=0.1,
depth_load_op=wgpu.LoadOp.clear,
depth_store_op=wgpu.StoreOp.store,
stencil_load_op=wgpu.LoadOp.clear,
stencil_store_op=wgpu.StoreOp.store,
)

command_encoder = device.create_command_encoder()
# This call to begin_render_pass may create warnings.
render_pass = command_encoder.begin_render_pass(
color_attachments=[], depth_stencil_attachment=depth_stencil_attachment
)
render_pass.end()

if "stencil" in depth_stencil_tex_format:
assert not warnings
else:
assert len(warnings) == 2
warnings.sort()
assert "stencil_load_op" in warnings[0]
assert "stencil_store_op" in warnings[1]
warnings.clear()

# The warnings are issued only once.
render_pass = command_encoder.begin_render_pass(
color_attachments=[], depth_stencil_attachment=depth_stencil_attachment
)
render_pass.end()
assert not warnings


if __name__ == "__main__":
run_tests(globals())
3 changes: 0 additions & 3 deletions tests/test_wgpu_occlusion_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,6 @@ def draw_square(result, x_offset=0.0, y_offset=0.0, z=0.5, reverse=False):
"depth_clear_value": 1.0,
"depth_load_op": "clear",
"depth_store_op": "store",
"stencil_clear_value": 1.0,
"stencil_load_op": "clear",
"stencil_store_op": "store",
}

render_pass = command_encoder.begin_render_pass(
Expand Down
Loading

0 comments on commit 68f2846

Please sign in to comment.