Skip to content

Commit

Permalink
Add tests and apply small tweaks/renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
almarklein committed Nov 18, 2024
1 parent 4aad851 commit 84c5541
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 25 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import rendercanvas # noqa: E402
import rendercanvas.stub # noqa: E402 - we use the stub backend to generate doccs
import rendercanvas._context # noqa: E402 - we use the ContexInterface to generate doccs
import rendercanvas.utils.bitmaptoscreenadapter # noqa: E402
import rendercanvas.utils.bitmappresentadapter # noqa: E402

# -- Project information -----------------------------------------------------

Expand Down
6 changes: 3 additions & 3 deletions docs/contextapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ on the CPU. All GPU API's have ways to do this.
download from gpu to cpu
If the context has a bitmap to present, and the canvas only supports presenting
to screen, you can usse a small utility: the ``BitmapToScreenAdapter`` takes a
to screen, you can usse a small utility: the ``BitmapPresentAdapter`` takes a
bitmap and presents it to the screen.

.. code-block::
Expand All @@ -54,7 +54,7 @@ bitmap and presents it to the screen.
──render──► | Context │ │ │ Canvas │
│ │ ──bitmap─┘ │ |
└─────────┘ └────────┘
use BitmapToScreenAdapter
use BitmapPresentAdapter
This way, contexts can be made to work with all canvas backens.

Expand Down Expand Up @@ -94,6 +94,6 @@ Also see https://github.com/pygfx/rendercanvas/blob/main/rendercanvas/_context.p
Adapter
-------

.. autoclass:: rendercanvas.utils.bitmaptoscreenadapter.BitmapToScreenAdapter
.. autoclass:: rendercanvas.utils.bitmappresentadapter.BitmapPresentAdapter
:members:
:no-index:
2 changes: 1 addition & 1 deletion rendercanvas/_context.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
A stub context imlpementation for documentation purposes.
A stub context implementation for documentation purposes.
It does actually work, but presents nothing.
"""

Expand Down
16 changes: 11 additions & 5 deletions rendercanvas/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def get_context(self, context_type):
* "wgpu": get a ``WgpuCanvasContext`` provided by the ``wgpu`` library.
* "bitmap": get a ``BitmapRenderingContext`` provided by the ``rendercanvas`` library.
* "another.module": other libraries may provide contexts too. We've only listed the ones we know of.
* "your.module:ContextClass": Explicit name.
Later calls to this method, with the same context_type argument, will return
the same context instance as was returned the first time the method was
Expand All @@ -155,6 +156,9 @@ def get_context(self, context_type):
# Note that this method is analog to HtmlCanvas.getContext(), except
# the context_type is different, since contexts are provided by other projects.

if not isinstance(context_type, str):
raise TypeError("context_type must be str.")

# Resolve the context type name
known_types = {
"wgpu": "wgpu",
Expand All @@ -172,23 +176,25 @@ def get_context(self, context_type):
)

# Load module
module_name, _, class_name = resolved_context_type.partition(":")
try:
module = importlib.import_module(resolved_context_type)
module = importlib.import_module(module_name)
except ImportError as err:
raise ValueError(
f"Cannot get context for '{context_type}': {err}.\nKnown valid values are {set(known_types)}"
f"Cannot get context for '{context_type}': {err}. Known valid values are {set(known_types)}"
) from None

# Obtain factory to produce context
factory_name = class_name or "rendercanvas_context_hook"
try:
hook_func = module.rendercanvas_context_hook
factory_func = getattr(module, factory_name)
except AttributeError:
raise ValueError(
f"Cannot get context for '{context_type}': could not find `rendercanvas_context_hook` in '{module.__name__}'"
f"Cannot get context for '{context_type}': could not find `{factory_name}` in '{module.__name__}'"
) from None

# Create the context
context = hook_func(self, self._rc_get_present_methods())
context = factory_func(self, self._rc_get_present_methods())

# Quick checks to make sure the context has the correct API
if not (hasattr(context, "canvas") and context.canvas is self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,14 @@
import wgpu


class BitmapToScreenAdapter:
"""An adapter to present a bitmap to screen (using wgpu).
class BitmapPresentAdapter:
"""An adapter to present a bitmap to a canvas using wgpu.
This adapter can be used by context objects that want to present a bitmap, when the
canvas only supoorts presenting to screen.
"""

def __init__(self, canvas, present_methods):
# We're going to pretend that the canvas can *only* present to screen, so we force wgpu to present to screen.
if "screen" not in present_methods:
raise RuntimeError(
"Cannot use BitmapToScreenAdapter if the canvas does not support presenting to screen"
)
present_methods = {"screen": present_methods["screen"]}

# Init wgpu
adapter = wgpu.gpu.request_adapter_sync(power_preference="high-performance")
device = self._device = adapter.request_device_sync(required_limits={})
Expand All @@ -37,19 +30,20 @@ def present_bitmap(self, bitmap):
"""Present the given bitmap to screen.
Supported formats are "rgba-u8" and "i-u8" (grayscale).
Returns the present-result dict produced by ``GPUCanvasContext.present()``.
"""

self._texture_helper.set_texture_data(bitmap)

if not self._context_is_configured:
self._context.configure(device=self._device, format=None)
self._context.configure(device=self._device, format="rgba8unorm")

target = self._context.get_current_texture().create_view()
command_encoder = self._device.create_command_encoder()
self._texture_helper.draw(command_encoder, target)
self._device.queue.submit([command_encoder.finish()])

self._context.present()
return self._context.present()


class FullscreenTexture:
Expand Down Expand Up @@ -339,9 +333,12 @@ def draw(self, command_encoder, target_texture_view):
} else if (uniforms.format == 2) {
color = vec4<f32>(value.r, value.r, value.r, value.g);
}
let physical_color = vec3<f32>(pow(color.rgb, vec3<f32>(2.2))); // gamma correct
// We assume that the input color is sRGB. We don't need to go to physical/linear
// colorspace, because we don't need light calculations or anything. The
// output texture format is a regular rgba8unorm (not srgb), so that no transform
// happens as we write to the texture; the pixel values are already srgb.
var out: FragmentOutput;
out.color = vec4<f32>(physical_color.rgb, color.a);
out.color = color;
return out;
}
"""
4 changes: 2 additions & 2 deletions rendercanvas/utils/bitmaprenderingcontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ def __init__(self, canvas, present_methods):
assert "screen" in present_methods or "bitmap" in present_methods
self._present_method = "bitmap" if "bitmap" in present_methods else "screen"
if self._present_method == "screen":
from rendercanvas.utils.bitmaptoscreenadapter import BitmapToScreenAdapter
from rendercanvas.utils.bitmappresentadapter import BitmapPresentAdapter

self._screen_adapter = BitmapToScreenAdapter(canvas, present_methods)
self._screen_adapter = BitmapPresentAdapter(canvas, present_methods)

self._bitmap_and_format = None

Expand Down
Loading

0 comments on commit 84c5541

Please sign in to comment.