Skip to content

Commit a38f0da

Browse files
authored
Add _result='texture' and documentation (#290)
* Add 'texture' as possible return_type * Add a test for _result='texture' * Add documentation on using _result= Fixes #69
1 parent 2840f19 commit a38f0da

File tree

4 files changed

+82
-9
lines changed

4 files changed

+82
-9
lines changed

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ If you use SlangPy in a research project leading to a publication, please cite t
5656
:caption: Basics
5757

5858
src/basics/firstfunctions
59+
src/basics/returntype
5960
src/basics/buffers
6061
src/basics/textures
6162
src/basics/nested

docs/src/basics/returntype.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
Return Types
2+
============
3+
4+
Slangpy can generate different types of container to hold the returned results of a Slang function. This is convenient for getting results in a preferred container type, such as a numpy array, texture, or tensor.
5+
6+
Let's start by reusing the example from :ref:`firstfunctions`.
7+
8+
Shader:
9+
10+
.. code-block::
11+
12+
// example.slang
13+
14+
// A simple function that adds two numbers together
15+
float add(float a, float b)
16+
{
17+
return a + b;
18+
}
19+
20+
In the original :ref:`firstfunctions` python example, we returned the result in a numpy array. Let's return it as a texture instead:
21+
22+
.. code-block:: python
23+
24+
## main.py
25+
26+
# ... initialization here ...
27+
28+
# Create a couple of buffers with 128x128 random floats
29+
a = np.random.rand(128, 128).astype(np.float32)
30+
b = np.random.rand(128, 128).astype(np.float32)
31+
32+
# Call our function and ask for a texture back
33+
result = module.add(a, b, _result='texture')
34+
35+
# Print the first 5x5 values
36+
print(result.to_numpy()[:5, :5])
37+
38+
# Display the result using tev
39+
spy.tev.show(result, name='add random')
40+
41+
Here we use ``_result`` to specify that we want the result to be a texture.
42+
43+
The ``_result`` can be ``'numpy'``, ``'texture'``, or ``'tensor'``. You can also use ``_result`` to specify the type directly, like ``numpy.ndarray``, ``slangpy.Texture``, or ``slangpy.Tensor``. Or you can reuse an existing variable of one of those types by passing it directly.
44+
45+
You'll see more examples using ``_result`` in the rest of this documentation!

slangpy/core/function.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ def return_type(self, return_type: Union[type, str]):
164164
from slangpy.types import Tensor
165165

166166
return_type = Tensor
167+
elif return_type == "texture":
168+
from slangpy import Texture
169+
170+
return_type = Texture
167171
else:
168172
raise ValueError(f"Unknown return type '{return_type}'")
169173
return FunctionNodeReturnType(self, return_type)

slangpy/tests/slangpy_tests/test_textures.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import numpy as np
33
import pytest
44
from slangpy import TextureDesc, TextureUsage
5+
from typing import Union
56

67
from . import helpers
78
from slangpy import InstanceBuffer, Module
@@ -274,7 +275,7 @@ def test_read_write_texture_with_resource_views(
274275
[TextureType.texture_1d, TextureType.texture_2d, TextureType.texture_3d],
275276
)
276277
@pytest.mark.parametrize("slices", [1, 4])
277-
@pytest.mark.parametrize("mips", [1])
278+
@pytest.mark.parametrize("mips", [ALL_MIPS, 1])
278279
@pytest.mark.parametrize("device_type", helpers.DEFAULT_DEVICE_TYPES)
279280
def test_copy_value(device_type: DeviceType, slices: int, mips: int, type: TextureType):
280281
m = load_test_module(device_type)
@@ -432,13 +433,13 @@ def test_texture_3d_shapes(device_type: DeviceType, shape: tuple[int, ...]):
432433
assert np.allclose(copied, tex_data)
433434

434435

435-
@pytest.mark.parametrize(
436-
"texel_name", ["uint8_t", "uint16_t", "int8_t", "int16_t", "float", "half", "uint"]
437-
)
438-
@pytest.mark.parametrize("dims", [1, 2, 3])
439-
@pytest.mark.parametrize("channels", [1, 2, 4])
440-
@pytest.mark.parametrize("device_type", helpers.DEFAULT_DEVICE_TYPES)
441-
def test_texture_return_value(device_type: DeviceType, texel_name: str, dims: int, channels: int):
436+
def texture_return_value_impl(
437+
device_type: DeviceType,
438+
texel_name: str,
439+
dims: int,
440+
channels: int,
441+
return_type: Union[str, type],
442+
):
442443
if texel_name in ("uint8_t", "int8_t") and device_type == DeviceType.d3d12:
443444
pytest.skip("8-bit types not supported by DXC")
444445

@@ -460,7 +461,7 @@ def test_texture_return_value(device_type: DeviceType, texel_name: str, dims: in
460461
buffer = NDBuffer(m.device, dtype, shape=shape)
461462
buffer.copy_from_numpy(data)
462463

463-
result = m.passthru.map(buffer.dtype)(buffer, _result=Texture)
464+
result = m.passthru.map(buffer.dtype)(buffer, _result=return_type)
464465

465466
assert isinstance(result, Texture)
466467
if dims == 1:
@@ -489,5 +490,27 @@ def test_texture_return_value(device_type: DeviceType, texel_name: str, dims: in
489490
assert np.allclose(result_np, data.squeeze())
490491

491492

493+
@pytest.mark.parametrize(
494+
"texel_name", ["uint8_t", "uint16_t", "int8_t", "int16_t", "float", "half", "uint"]
495+
)
496+
@pytest.mark.parametrize("dims", [1, 2, 3])
497+
@pytest.mark.parametrize("channels", [1, 2, 4])
498+
@pytest.mark.parametrize("device_type", helpers.DEFAULT_DEVICE_TYPES)
499+
def test_texture_return_value(device_type: DeviceType, texel_name: str, dims: int, channels: int):
500+
texture_return_value_impl(device_type, texel_name, dims, channels, Texture)
501+
502+
503+
# This case checks for when the return type is the string "texture".
504+
# This checks a subset of the "test_texture_return_value" parameters.
505+
@pytest.mark.parametrize("texel_name", ["float"])
506+
@pytest.mark.parametrize("dims", [1, 2, 3])
507+
@pytest.mark.parametrize("channels", [4])
508+
@pytest.mark.parametrize("device_type", helpers.DEFAULT_DEVICE_TYPES)
509+
def test_texture_return_value_str(
510+
device_type: DeviceType, texel_name: str, dims: int, channels: int
511+
):
512+
texture_return_value_impl(device_type, texel_name, dims, channels, "texture")
513+
514+
492515
if __name__ == "__main__":
493516
pytest.main([__file__, "-v", "-s"])

0 commit comments

Comments
 (0)