diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index a5bf6fceb..efe72c2d2 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -32,7 +32,9 @@ env: libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev libfreetype6-dev libx11-dev libxcomposite-dev libxcursor-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxrandr-dev libxrender-dev libxfixes-dev libxss-dev libxtst-dev libxkbcommon-dev - libglu1-mesa-dev libegl1-mesa-dev mesa-common-dev + libglu1-mesa-dev libegl1-mesa-dev mesa-common-dev libgl1-mesa-dri mesa-utils xvfb + LIBGL_ALWAYS_SOFTWARE: "1" + GALLIUM_DRIVER: "llvmpipe" jobs: configure: @@ -43,6 +45,8 @@ jobs: fetch-depth: 0 - name: Install Dependencies run: sudo apt-get update && sudo apt-get install -y ${INSTALL_DEPS} + - name: Test OpenGL Version + run: xvfb-run glxinfo | grep "OpenGL version" - name: Configure run: cmake ${{ github.workspace }} -G "Ninja Multi-Config" -B ${{ runner.workspace }}/build -DYUP_ENABLE_TESTS=ON -DYUP_ENABLE_EXAMPLES=ON - name: Build SDL @@ -75,7 +79,7 @@ jobs: run: cmake ${{ github.workspace }} -G "Ninja Multi-Config" -B ${{ runner.workspace }}/build -DYUP_ENABLE_TESTS=ON - run: cmake --build ${{ runner.workspace }}/build --config Debug --target yup_tests - working-directory: ${{ runner.workspace }}/build/tests/Debug - run: ./yup_tests + run: xvfb-run -a ./yup_tests build_tests_release: runs-on: ubuntu-latest @@ -96,7 +100,7 @@ jobs: run: cmake ${{ github.workspace }} -G "Ninja Multi-Config" -B ${{ runner.workspace }}/build -DYUP_ENABLE_TESTS=ON - run: cmake --build ${{ runner.workspace }}/build --config Release --target yup_tests - working-directory: ${{ runner.workspace }}/build/tests/Release - run: ./yup_tests + run: xvfb-run -a ./yup_tests build_console: runs-on: ubuntu-latest diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 79eca136e..85dd1512e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -64,7 +64,9 @@ env: libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev libfreetype6-dev libx11-dev libxcomposite-dev libxcursor-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxrandr-dev libxrender-dev libxfixes-dev libxss-dev libxtst-dev libxkbcommon-dev - libglu1-mesa-dev libegl1-mesa-dev mesa-common-dev lcov + libglu1-mesa-dev libegl1-mesa-dev mesa-common-dev libgl1-mesa-dri xvfb lcov + LIBGL_ALWAYS_SOFTWARE: "1" + GALLIUM_DRIVER: "llvmpipe" IGNORE_ERRORS: "mismatch,gcov,source,negative,unused,empty,format,corrupt" jobs: @@ -94,7 +96,7 @@ jobs: run: cmake --build . --target coverage_clean - name: Run C++ Tests working-directory: ${{ runner.workspace }}/build/tests/Debug - run: SDL_VIDEODRIVER=dummy ./yup_tests --gtest_output=xml:test_results.xml + run: xvfb-run -a ./yup_tests - name: Generate C++ Coverage Report working-directory: ${{ runner.workspace }}/build run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d2e6062e..a319cc4f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Added a native WebGPU `GraphicsContext` backend for Emscripten via the Emdawnwebgpu port (`RIVE_WEBGPU=2` + `--use-port=emdawnwebgpu`, enabled with the `ENABLE_EMSCRIPTEN_WEBGPU` parameter of `yup_standalone_app`), rendering Rive content through the browser's WebGPU API without Dawn - Fixed `GpuFrame::begin()` aborting on the Emscripten WebGPU backend: the WGPU context now creates and submits its own command encoder when no external one is provided, matching the Metal/GL/D3D11 self-managed frame model +- Fixed a crash on Windows when creating any native window: the D3D11 `GpuDevice` was built with an already moved-from `ID3D11Device`, and the Direct3D `GraphicsContext` created a second device whose swapchain textures could not be used by the render context. Both now share a single `ID3D11Device` +- Fixed the Emscripten WebGPU `GraphicsContext` never storing its surface size, leaving the offscreen copy at 0x0 #### Rive Runtime Bump diff --git a/codecov.yml b/codecov.yml index 4f79ea66e..8a691dd12 100644 --- a/codecov.yml +++ b/codecov.yml @@ -10,6 +10,7 @@ coverage: project: default: informational: true + if_ci_failed: error target: 80% threshold: 5% base: auto @@ -30,11 +31,13 @@ coverage: - yup_graphics - yup_gui - yup_python + - yup_rhi - yup_shading - yup_simd patch: default: informational: true + if_ci_failed: error target: 80% threshold: 5% @@ -55,6 +58,7 @@ flags: yup_graphics: { paths: [modules/yup_graphics/], carryforward: true } yup_gui: { paths: [modules/yup_gui/], carryforward: true } yup_python: { paths: [modules/yup_python/], carryforward: true } + yup_rhi: { paths: [modules/yup_rhi/], carryforward: true } yup_shading: { paths: [modules/yup_shading/], carryforward: true } yup_simd: { paths: [modules/yup_simd/], carryforward: true } diff --git a/docs/index.md b/docs/index.md index d5bbbdbca..93b2ae059 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,6 +27,7 @@ concept guides, walkthroughs, and reference material. - [Imaging](imaging/index.md) - bitmap images: pixels, loading, saving, and drawing. - [UI](ui/index.md) - components, windowing, events, layout, and widgets. - [Audio](audio/index.md) - audio devices, formats, DSP, the audio graph, processors, and plugin hosting/client wrappers. +- [AI](ai/index.md) - the AI and LLM infrastructure. - [Scripting](scripting/index.md) - the Python bindings layer. ## Quick links diff --git a/justfile b/justfile index 9ada9de3b..8a2b9addc 100644 --- a/justfile +++ b/justfile @@ -118,3 +118,12 @@ shader_bundler *COMPILE_ARGS: rive_update REF="runtime-v0.1.62": uv run python tools/rive_update.py --rive-ref {{REF}} --allow-dirty --keep-work-dir + +rive_shaders_update: + uv venv .venv --clear + source .venv/bin/activate + uv pip install ply + uv run make -C thirdparty/rive_renderer/source/shaders -j 8 + cp -R thirdparty/rive_renderer/source/shaders/out/generated/* thirdparty/rive_renderer/source/generated/shaders/ + rm -Rf thirdparty/rive_renderer/source/shaders/out + .venv/bin/deactivate diff --git a/modules/yup_graphics/context/yup_GraphicsContext.cpp b/modules/yup_graphics/context/yup_GraphicsContext.cpp index 327fac63e..058303dc3 100644 --- a/modules/yup_graphics/context/yup_GraphicsContext.cpp +++ b/modules/yup_graphics/context/yup_GraphicsContext.cpp @@ -26,7 +26,7 @@ namespace yup bool GraphicsContext::isGpuAvailable() const noexcept { if (auto device = getGpuDevice()) - return device->gpuContext() != nullptr; + return device->getGpuContext() != nullptr; return false; } diff --git a/modules/yup_graphics/context/yup_GraphicsContext.h b/modules/yup_graphics/context/yup_GraphicsContext.h index 80942d6e9..8280f6527 100644 --- a/modules/yup_graphics/context/yup_GraphicsContext.h +++ b/modules/yup_graphics/context/yup_GraphicsContext.h @@ -89,19 +89,19 @@ class YUP_API GraphicsContext @return Pointer to a rive::Factory object. */ - virtual rive::Factory* factory() = 0; + virtual rive::Factory* getFactory() = 0; /** Gets the PLS render context, if available. @return Pointer to a rive::pls::PLSRenderContext, or nullptr if not available. */ - virtual rive::gpu::RenderContext* renderContext() = 0; + virtual rive::gpu::RenderContext* getRenderContext() = 0; /** Gets the PLS render target, if available. @return Pointer to a rive::pls::PLSRenderTarget, or nullptr if not available. */ - virtual rive::gpu::RenderTarget* renderTarget() = 0; + virtual rive::gpu::RenderTarget* getRenderTarget() = 0; /** Creates a renderer suitable for the specified dimensions. diff --git a/modules/yup_graphics/formats/yup_JpegImageFormat.cpp b/modules/yup_graphics/formats/yup_JpegImageFormat.cpp index 97f6508a3..72c1dbac9 100644 --- a/modules/yup_graphics/formats/yup_JpegImageFormat.cpp +++ b/modules/yup_graphics/formats/yup_JpegImageFormat.cpp @@ -148,7 +148,7 @@ JpegImageFormatReader::JpegImageFormatReader (InputStream* stream, const ImageFo metadata->rawChunks["jpeg/exif"] = MemoryBlock (data + 6, dataLength - 6); } else if (marker->marker == JPEG_APP0 + 1 && dataLength > 29 - && std::memcmp (data, "http://ns.adobe.com/xap/", 29) == 0) + && std::memcmp (data, "http://ns.adobe.com/xap/1.0/", 29) == 0) { if (getOptions().parseRawChunks) metadata->rawChunks["jpeg/xmp"] = MemoryBlock (data, dataLength); diff --git a/modules/yup_graphics/formats/yup_PngImageFormat.cpp b/modules/yup_graphics/formats/yup_PngImageFormat.cpp index 8955a72c3..5ca22286b 100644 --- a/modules/yup_graphics/formats/yup_PngImageFormat.cpp +++ b/modules/yup_graphics/formats/yup_PngImageFormat.cpp @@ -372,7 +372,7 @@ void PngImageFormatReader::parseChunks() // tIME chunk if (std::memcmp (type, "tIME", 4) == 0 && chunkLen >= 7) { - char buf[20]; + char buf[32]; snprintf (buf, sizeof (buf), "%04d:%02d:%02d %02d:%02d:%02d", readBE16 (chunkData), chunkData[2], chunkData[3], chunkData[4], chunkData[5], chunkData[6]); metadata->textEntries.set ("png/time", String (buf)); } diff --git a/modules/yup_graphics/graphics/yup_Graphics.cpp b/modules/yup_graphics/graphics/yup_Graphics.cpp index 59dac52d6..13761f13c 100644 --- a/modules/yup_graphics/graphics/yup_Graphics.cpp +++ b/modules/yup_graphics/graphics/yup_Graphics.cpp @@ -194,7 +194,7 @@ rive::Factory* getOffscreenFactory (GraphicsContext& context, RenderableTarget* if (auto* renderContext = target->getRenderContext()) return renderContext; - return context.factory(); + return context.getFactory(); } std::unique_ptr makeOffscreenRenderer (GraphicsContext& context, RenderableTarget* target, int width, int height) @@ -240,7 +240,7 @@ void Graphics::SavedState::restore() Graphics::Graphics (GraphicsContext& context, rive::Renderer& renderer, float scale) noexcept : context (context) , offscreenTarget (nullptr) - , factory (*context.factory()) + , factory (*context.getFactory()) , ownedRenderer (nullptr) , renderer (renderer) , contextScale (scale) @@ -939,7 +939,7 @@ void Graphics::drawTexture (const GpuTexture::Ptr& texture, const Rectangle texture, const Rectangle& targetArea) { - auto renderContext = context.renderContext(); + auto renderContext = context.getRenderContext(); if (renderContext == nullptr || texture == nullptr) return false; diff --git a/modules/yup_graphics/imaging/yup_Image.cpp b/modules/yup_graphics/imaging/yup_Image.cpp index 66477b2d8..c08002efb 100644 --- a/modules/yup_graphics/imaging/yup_Image.cpp +++ b/modules/yup_graphics/imaging/yup_Image.cpp @@ -249,7 +249,7 @@ bool Image::createTextureIfNotPresent (GraphicsContext& context) const auto width = getWidth(); auto height = getHeight(); - auto renderContext = context.renderContext(); + auto renderContext = context.getRenderContext(); if (renderContext == nullptr || renderContext->impl() == nullptr) return false; diff --git a/modules/yup_graphics/imaging/yup_ImagePixelData.cpp b/modules/yup_graphics/imaging/yup_ImagePixelData.cpp index b537d5070..b953a161c 100644 --- a/modules/yup_graphics/imaging/yup_ImagePixelData.cpp +++ b/modules/yup_graphics/imaging/yup_ImagePixelData.cpp @@ -50,17 +50,17 @@ std::vector ImagePixelData::toRGBA (bool premultiplyAlpha) const switch (format) { case PixelFormat::Grayscale: - ColorVectorOperations::convertGrayscaleToRGBA (src, result.data(), numPixels); + ColorVectorOperations::convertGrayscaleToRGBA (src, reinterpret_cast (result.data()), numPixels); break; case PixelFormat::RGB: - ColorVectorOperations::convertRGBToRGBA (src, result.data(), numPixels); + ColorVectorOperations::convertRGBToRGBA (src, reinterpret_cast (result.data()), numPixels); break; case PixelFormat::RGBA: std::memcpy (result.data(), src, result.size()); if (premultiplyAlpha) - ColorVectorOperations::premultiplyRGBA (result.data(), numPixels); + ColorVectorOperations::premultiplyRGBA (reinterpret_cast (result.data()), numPixels); break; } diff --git a/modules/yup_graphics/native/yup_GraphicsContext_d3d.cpp b/modules/yup_graphics/native/yup_GraphicsContext_d3d.cpp index 94ced0c71..aa3f08316 100644 --- a/modules/yup_graphics/native/yup_GraphicsContext_d3d.cpp +++ b/modules/yup_graphics/native/yup_GraphicsContext_d3d.cpp @@ -28,44 +28,44 @@ namespace yup { -class LowLevelRenderContextD3D : public GraphicsContext +ID3D11Device* yup_getDirect3DDevice (GpuDevice&); +ID3D11DeviceContext* yup_getDirect3DDeviceContext (GpuDevice&); + +//============================================================================== + +class GraphicsContextD3D : public GraphicsContext { public: - LowLevelRenderContextD3D (ComPtr d3dFactory, - ComPtr gpu, - ComPtr gpuContext, - bool isHeadless, - const rive::gpu::D3DContextOptions& contextOptions, - Options options, - GpuDevice::Ptr existingGpu = {}) - : m_isHeadless (isHeadless) - , m_options (options) - , m_d3dFactory (std::move (d3dFactory)) - , m_gpu (std::move (gpu)) - , m_gpuContext (std::move (gpuContext)) - , m_renderContext (rive::gpu::RenderContextD3DImpl::MakeContext (m_gpu, m_gpuContext, contextOptions)) + GraphicsContextD3D (ComPtr d3dFactory, + ComPtr device, + ComPtr deviceContext, + bool isHeadless, + Options options, + GpuDevice::Ptr gpuDevice) + : isHeadless (isHeadless) + , options (options) + , gpuDevice (std::move (gpuDevice)) + , d3dFactory (std::move (d3dFactory)) + , device (std::move (device)) + , deviceContext (std::move (deviceContext)) { - if (existingGpu != nullptr) - m_gpuContextPtr = std::move (existingGpu); - else - m_gpuContextPtr = GpuDevice::create (GpuPlatform::Direct3D, options); } GpuPlatform getPlatform() const noexcept override { return GpuPlatform::Direct3D; } - GpuDevice::Ptr getGpuDevice() const noexcept override { return m_gpuContextPtr; } + GpuDevice::Ptr getGpuDevice() const noexcept override { return gpuDevice; } - rive::Factory* factory() override { return m_renderContext.get(); } + rive::Factory* getFactory() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderContext* renderContext() override { return m_renderContext.get(); } + rive::gpu::RenderContext* getRenderContext() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderTarget* renderTarget() override { return m_renderTarget.get(); } + rive::gpu::RenderTarget* getRenderTarget() override { return renderTarget.get(); } void onSizeChanged (void* window, int width, int height, float dpiScale, uint32_t sampleCount) override { - if (! m_isHeadless) + if (! isHeadless) { - m_swapchain.Reset(); + swapchain.Reset(); DXGI_SWAP_CHAIN_DESC1 scd {}; scd.Width = width; scd.Height = height; @@ -75,12 +75,12 @@ class LowLevelRenderContextD3D : public GraphicsContext scd.BufferCount = 2; scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - VERIFY_OK (m_d3dFactory->CreateSwapChainForHwnd (m_gpu.Get(), - (HWND) window, - &scd, - nullptr, - nullptr, - m_swapchain.ReleaseAndGetAddressOf())); + VERIFY_OK (d3dFactory->CreateSwapChainForHwnd (device.Get(), + (HWND) window, + &scd, + nullptr, + nullptr, + swapchain.ReleaseAndGetAddressOf())); } else { @@ -95,55 +95,55 @@ class LowLevelRenderContextD3D : public GraphicsContext desc.BindFlags = D3D11_BIND_RENDER_TARGET; desc.CPUAccessFlags = 0; desc.MiscFlags = 0; - VERIFY_OK (m_gpu->CreateTexture2D (&desc, nullptr, &m_headlessDrawTexture)); + VERIFY_OK (device->CreateTexture2D (&desc, nullptr, &headlessDrawTexture)); } - auto renderContextImpl = m_renderContext->static_impl_cast(); - m_renderTarget = renderContextImpl->makeRenderTarget (width, height); - m_readbackTexture = nullptr; + auto renderContextImpl = getRenderContext()->static_impl_cast(); + renderTarget = renderContextImpl->makeRenderTarget (width, height); + readbackTexture = nullptr; } std::unique_ptr makeRenderer (int width, int height) override { - return std::make_unique (m_renderContext.get()); + return std::make_unique (getRenderContext()); } void begin (const rive::gpu::RenderContext::FrameDescriptor& frameDescriptor) override { - m_renderContext->beginFrame (frameDescriptor); + getRenderContext()->beginFrame (frameDescriptor); } void end (void*) override { - if (m_renderTarget->targetTexture() == nullptr) + if (renderTarget->targetTexture() == nullptr) { - if (m_isHeadless) - m_renderTarget->setTargetTexture (m_headlessDrawTexture); + if (isHeadless) + renderTarget->setTargetTexture (headlessDrawTexture); else { ComPtr backbuffer; - HRESULT hr = m_swapchain->GetBuffer (0, __uuidof (ID3D11Texture2D), reinterpret_cast (backbuffer.ReleaseAndGetAddressOf())); + HRESULT hr = swapchain->GetBuffer (0, __uuidof (ID3D11Texture2D), reinterpret_cast (backbuffer.ReleaseAndGetAddressOf())); if (FAILED (hr)) { - auto reason = m_gpu->GetDeviceRemovedReason(); + auto reason = device->GetDeviceRemovedReason(); fprintf (stderr, "D3D: GetBuffer failed: hr=0x%08X, deviceRemovedReason=0x%08X\n", static_cast (hr), static_cast (reason)); - m_renderTarget->setTargetTexture (nullptr); + renderTarget->setTargetTexture (nullptr); return; } - m_renderTarget->setTargetTexture (backbuffer); + renderTarget->setTargetTexture (backbuffer); } } rive::gpu::RenderContext::FlushResources flushDesc; - flushDesc.renderTarget = m_renderTarget.get(); - m_renderContext->flush (flushDesc); + flushDesc.renderTarget = renderTarget.get(); + getRenderContext()->flush (flushDesc); - if (! m_isHeadless) + if (! isHeadless) { - HRESULT hr = m_swapchain->Present (0, 0); + HRESULT hr = swapchain->Present (0, 0); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { - auto reason = m_gpu->GetDeviceRemovedReason(); + auto reason = device->GetDeviceRemovedReason(); fprintf (stderr, "D3D: Present returned device removed/reset: hr=0x%08X, deviceRemovedReason=0x%08X\n", static_cast (hr), static_cast (reason)); } else if (FAILED (hr)) @@ -152,63 +152,43 @@ class LowLevelRenderContextD3D : public GraphicsContext } } - m_renderTarget->setTargetTexture (nullptr); + renderTarget->setTargetTexture (nullptr); } private: - const bool m_isHeadless; - Options m_options; - ComPtr m_d3dFactory; - ComPtr m_gpu; - ComPtr m_gpuContext; - ComPtr m_swapchain; - ComPtr m_readbackTexture; - ComPtr m_headlessDrawTexture; - GpuDevice::Ptr m_gpuContextPtr; - std::unique_ptr m_renderContext; - rive::rcp m_renderTarget; + const bool isHeadless; + Options options; + GpuDevice::Ptr gpuDevice; + ComPtr d3dFactory; + ComPtr device; + ComPtr deviceContext; + ComPtr swapchain; + ComPtr readbackTexture; + ComPtr headlessDrawTexture; + rive::rcp renderTarget; }; -std::unique_ptr yup_constructDirect3DGraphicsContext (GpuDevice::Options fiddleOptions, GpuDevice::Ptr existingGpu) +std::unique_ptr yup_constructDirect3DGraphicsContext (GpuDevice::Options options, GpuDevice::Ptr existingGpu) { - ComPtr factory; - VERIFY_OK (CreateDXGIFactory (__uuidof (IDXGIFactory2), reinterpret_cast (factory.ReleaseAndGetAddressOf()))); - - ComPtr adapter; - DXGI_ADAPTER_DESC adapterDesc {}; - rive::gpu::D3DContextOptions contextOptions; - - if (fiddleOptions.disableRasterOrdering) - { - contextOptions.disableRasterizerOrderedViews = true; - contextOptions.disableTypedUAVLoadStore = true; - } - - for (UINT i = 0; factory->EnumAdapters (i, &adapter) != DXGI_ERROR_NOT_FOUND; ++i) - { - adapter->GetDesc (&adapterDesc); - contextOptions.isIntel = adapterDesc.VendorId == 0x163C || adapterDesc.VendorId == 0x8086 || adapterDesc.VendorId == 0x8087; - break; - } - - ComPtr gpu; - ComPtr gpuContext; - D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1 }; - - UINT creationFlags = 0; -#ifdef DEBUG - creationFlags |= D3D11_CREATE_DEVICE_DEBUG; -#endif + // The swapchain (and the textures obtained from it) must belong to the very same ID3D11Device + // that owns the render context drawing into them, so the GpuDevice is resolved first and its + // native device is reused here instead of creating a second one. + auto gpuDevice = existingGpu != nullptr ? std::move (existingGpu) + : GpuDevice::create (GpuPlatform::Direct3D, options); + if (gpuDevice == nullptr) + return nullptr; - VERIFY_OK (D3D11CreateDevice (adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, creationFlags, featureLevels, std::size (featureLevels), D3D11_SDK_VERSION, gpu.ReleaseAndGetAddressOf(), nullptr, gpuContext.ReleaseAndGetAddressOf())); + ComPtr device = yup_getDirect3DDevice (*gpuDevice); + ComPtr deviceContext = yup_getDirect3DDeviceContext (*gpuDevice); - if (! gpu || ! gpuContext) + if (! device || ! deviceContext) return nullptr; - printf ("D3D device: %S\n", adapterDesc.Description); + ComPtr factory; + VERIFY_OK (CreateDXGIFactory (__uuidof (IDXGIFactory2), reinterpret_cast (factory.ReleaseAndGetAddressOf()))); - return std::make_unique ( - std::move (factory), std::move (gpu), std::move (gpuContext), fiddleOptions.allowHeadlessRendering, contextOptions, fiddleOptions, std::move (existingGpu)); + return std::make_unique ( + std::move (factory), std::move (device), std::move (deviceContext), options.allowHeadlessRendering, options, std::move (gpuDevice)); } } // namespace yup diff --git a/modules/yup_graphics/native/yup_GraphicsContext_dawn.cpp b/modules/yup_graphics/native/yup_GraphicsContext_dawn.cpp index 0a4ffac69..477c8f353 100644 --- a/modules/yup_graphics/native/yup_GraphicsContext_dawn.cpp +++ b/modules/yup_graphics/native/yup_GraphicsContext_dawn.cpp @@ -88,27 +88,27 @@ static std::unique_ptr SetupDawnWindowAndGetSurfaceDescript } #endif -class LowLevelRenderContextDawnPLS : public GraphicsContext +class GraphicsContextDawn : public GraphicsContext { public: - LowLevelRenderContextDawnPLS (Options options, GpuDevice::Ptr existingGpu = {}) - : m_options (options) + GraphicsContextDawn (Options options, GpuDevice::Ptr existingGpu = {}) + : options (options) { // Obtain or create the GpuDevice if (existingGpu != nullptr) - m_gpuContext = std::move (existingGpu); + gpuDevice = std::move (existingGpu); else - m_gpuContext = GpuDevice::create (GpuPlatform::WebGPU, options); + gpuDevice = GpuDevice::create (GpuPlatform::WebGPU, options); WGPUInstanceDescriptor instanceDescriptor {}; instanceDescriptor.features.timedWaitAnyEnable = true; - m_instance = std::make_unique (&instanceDescriptor); + instance = std::make_unique (&instanceDescriptor); wgpu::RequestAdapterOptions adapterOptions = { .powerPreference = wgpu::PowerPreference::HighPerformance, }; - auto adapters = m_instance->EnumerateAdapters (&adapterOptions); + auto adapters = instance->EnumerateAdapters (&adapterOptions); wgpu::DawnAdapterPropertiesPowerPreference power_props {}; wgpu::AdapterProperties adapterProperties {}; @@ -146,39 +146,39 @@ class LowLevelRenderContextDawnPLS : public GraphicsContext .requiredFeatures = requiredFeatures.data(), }; - m_backendDevice = preferredAdapter->CreateDevice (&deviceDesc); + backendDevice = preferredAdapter->CreateDevice (&deviceDesc); DawnProcTable backendProcs = dawn::native::GetProcs(); dawnProcSetProcs (&backendProcs); - backendProcs.deviceSetUncapturedErrorCallback (m_backendDevice, print_device_error, nullptr); - backendProcs.deviceSetDeviceLostCallback (m_backendDevice, device_lost_callback, nullptr); - backendProcs.deviceSetLoggingCallback (m_backendDevice, device_log_callback, nullptr); - - m_device = wgpu::Device::Acquire (m_backendDevice); - m_queue = m_device.GetQueue(); - m_plsContext = PLSRenderContextWebGPUImpl::MakeContext ( - m_device, m_queue, PLSRenderContextWebGPUImpl::ContextOptions()); + backendProcs.deviceSetUncapturedErrorCallback (backendDevice, print_device_error, nullptr); + backendProcs.deviceSetDeviceLostCallback (backendDevice, device_lost_callback, nullptr); + backendProcs.deviceSetLoggingCallback (backendDevice, device_log_callback, nullptr); + + device = wgpu::Device::Acquire (backendDevice); + queue = device.GetQueue(); + plsContext = PLSRenderContextWebGPUImpl::MakeContext ( + device, queue, PLSRenderContextWebGPUImpl::ContextOptions()); } GpuPlatform getPlatform() const noexcept override { return GpuPlatform::WebGPU; } - GpuDevice::Ptr getGpuDevice() const noexcept override { return m_gpuContext; } + GpuDevice::Ptr getGpuDevice() const noexcept override { return gpuDevice; } - Factory* factory() override { return m_plsContext.get(); } + Factory* getFactory() override { return plsContext.get(); } - rive::pls::PLSRenderContext* renderContext() override { return m_plsContext.get(); } + rive::gpu::RenderContext* getRenderContext() override { return plsContext.get(); } - rive::pls::PLSRenderTarget* renderTarget() override { return m_renderTarget.get(); } + rive::gpu::RenderTarget* getRenderTarget() override { return renderTarget.get(); } void onSizeChanged (void* window, int width, int height, float dpiScale, uint32_t sampleCount) override { DawnProcTable backendProcs = dawn::native::GetProcs(); - auto surfaceChainedDesc = SetupDawnWindowAndGetSurfaceDescriptor (window, m_options.retinaDisplay); + auto surfaceChainedDesc = SetupDawnWindowAndGetSurfaceDescriptor (window, options.retinaDisplay); WGPUSurfaceDescriptor surfaceDesc = { .nextInChain = reinterpret_cast (surfaceChainedDesc.get()), }; - WGPUSurface surface = backendProcs.instanceCreateSurface (m_instance->Get(), &surfaceDesc); + WGPUSurface surface = backendProcs.instanceCreateSurface (instance->Get(), &surfaceDesc); WGPUSwapChainDescriptor swapChainDesc = { .usage = WGPUTextureUsage_RenderAttachment, @@ -188,56 +188,56 @@ class LowLevelRenderContextDawnPLS : public GraphicsContext .presentMode = WGPUPresentMode_Immediate, }; - if (m_options.enableReadPixels) + if (options.readableFramebuffer) swapChainDesc.usage |= WGPUTextureUsage_CopySrc; - WGPUSwapChain backendSwapChain = backendProcs.deviceCreateSwapChain (m_backendDevice, surface, &swapChainDesc); - m_swapchain = wgpu::SwapChain::Acquire (backendSwapChain); + WGPUSwapChain backendSwapChain = backendProcs.deviceCreateSwapChain (backendDevice, surface, &swapChainDesc); + swapchain = wgpu::SwapChain::Acquire (backendSwapChain); - m_renderTarget = m_plsContext->static_impl_cast() - ->makeRenderTarget (wgpu::TextureFormat::BGRA8Unorm, width, height); + renderTarget = plsContext->static_impl_cast() + ->makeRenderTarget (wgpu::TextureFormat::BGRA8Unorm, width, height); - m_pixelReadBuff = {}; + pixelReadBuff = {}; } std::unique_ptr makeRenderer (int width, int height) override { - return std::make_unique (m_plsContext.get()); + return std::make_unique (plsContext.get()); } void begin (PLSRenderContext::FrameDescriptor&& frameDescriptor) override { - assert (m_swapchain.GetCurrentTexture().GetWidth() == m_renderTarget->width()); - assert (m_swapchain.GetCurrentTexture().GetHeight() == m_renderTarget->height()); - m_renderTarget->setTargetTextureView (m_swapchain.GetCurrentTextureView()); - frameDescriptor.renderTarget = m_renderTarget; - m_plsContext->beginFrame (std::move (frameDescriptor)); + assert (swapchain.GetCurrentTexture().GetWidth() == renderTarget->width()); + assert (swapchain.GetCurrentTexture().GetHeight() == renderTarget->height()); + renderTarget->setTargetTextureView (swapchain.GetCurrentTextureView()); + frameDescriptor.renderTarget = renderTarget; + plsContext->beginFrame (std::move (frameDescriptor)); } void end (void* window) override { - m_plsContext->flush(); - m_swapchain.Present(); + plsContext->flush(); + swapchain.Present(); } - void tick() override { m_device.Tick(); } + void tick() override { device.Tick(); } private: - Options m_options; - GpuDevice::Ptr m_gpuContext; - WGPUDevice m_backendDevice = {}; - wgpu::Device m_device = {}; - wgpu::Queue m_queue = {}; - wgpu::SwapChain m_swapchain = {}; - std::unique_ptr m_instance; - std::unique_ptr m_plsContext; - rcp m_renderTarget; - wgpu::Buffer m_pixelReadBuff; + Options options; + GpuDevice::Ptr gpuDevice; + WGPUDevice backendDevice = {}; + wgpu::Device device = {}; + wgpu::Queue queue = {}; + wgpu::SwapChain swapchain = {}; + std::unique_ptr instance; + std::unique_ptr plsContext; + rcp renderTarget; + wgpu::Buffer pixelReadBuff; }; std::unique_ptr yup_constructDawnGraphicsContext (GpuDevice::Options options, GpuDevice::Ptr existingGpu) { - return std::make_unique (options, std::move (existingGpu)); + return std::make_unique (options, std::move (existingGpu)); } } // namespace yup diff --git a/modules/yup_graphics/native/yup_GraphicsContext_dawn_helper.cpp b/modules/yup_graphics/native/yup_GraphicsContext_dawn_helper.cpp index f4882d206..a95fd9404 100644 --- a/modules/yup_graphics/native/yup_GraphicsContext_dawn_helper.cpp +++ b/modules/yup_graphics/native/yup_GraphicsContext_dawn_helper.cpp @@ -58,7 +58,7 @@ float GetDawnWindowBackingScaleFactor (void* window, bool retina) std::unique_ptr SetupDawnWindowAndGetSurfaceDescriptor (void* window, bool retina) { - @autoreleasepool + YUP_AUTORELEASEPOOL { NSWindow* nsWindow = (__bridge NSWindow*) window; NSView* view = [nsWindow contentView]; diff --git a/modules/yup_graphics/native/yup_GraphicsContext_headless.cpp b/modules/yup_graphics/native/yup_GraphicsContext_headless.cpp index 155b6b45a..16203e20b 100644 --- a/modules/yup_graphics/native/yup_GraphicsContext_headless.cpp +++ b/modules/yup_graphics/native/yup_GraphicsContext_headless.cpp @@ -185,60 +185,42 @@ class NoOpRenderer : public rive::Renderer //============================================================================== -class NoOpGraphicsContext : public GraphicsContext +class GraphicsContextHeadless : public GraphicsContext { public: - NoOpGraphicsContext() + GraphicsContextHeadless() + : gpuDevice (GpuDevice::create (GpuPlatform::Headless, {})) { - gpuCtx = GpuDevice::create (GpuPlatform::Headless, {}); } GpuPlatform getPlatform() const noexcept override { return GpuPlatform::Headless; } - GpuDevice::Ptr getGpuDevice() const noexcept override { return gpuCtx; } + GpuDevice::Ptr getGpuDevice() const noexcept override { return gpuDevice; } - rive::Factory* factory() override - { - return std::addressof (noOpFactory); - } + rive::Factory* getFactory() override { return std::addressof (noOpFactory); } - rive::gpu::RenderContext* renderContext() override - { - return nullptr; - } + rive::gpu::RenderContext* getRenderContext() override { return nullptr; } - rive::gpu::RenderTarget* renderTarget() override - { - return nullptr; - } + rive::gpu::RenderTarget* getRenderTarget() override { return nullptr; } - std::unique_ptr makeRenderer (int, int) override - { - return std::make_unique(); - } + std::unique_ptr makeRenderer (int, int) override { return std::make_unique(); } - void onSizeChanged (void*, int, int, float, uint32_t) override - { - } + void onSizeChanged (void*, int, int, float, uint32_t) override {} - void begin (const rive::gpu::RenderContext::FrameDescriptor&) override - { - } + void begin (const rive::gpu::RenderContext::FrameDescriptor&) override {} - void end (void*) override - { - } + void end (void*) override {} private: NoOpFactory noOpFactory; - GpuDevice::Ptr gpuCtx; + GpuDevice::Ptr gpuDevice; }; //============================================================================== -std::unique_ptr yup_constructHeadlessGraphicsContext (GpuDevice::Options fiddleOptions, GpuDevice::Ptr) +std::unique_ptr yup_constructHeadlessGraphicsContext (GpuDevice::Options, GpuDevice::Ptr) { - return std::make_unique(); + return std::make_unique(); } } // namespace yup diff --git a/modules/yup_graphics/native/yup_GraphicsContext_metal.cpp b/modules/yup_graphics/native/yup_GraphicsContext_metal.cpp index 408374d9b..310880431 100644 --- a/modules/yup_graphics/native/yup_GraphicsContext_metal.cpp +++ b/modules/yup_graphics/native/yup_GraphicsContext_metal.cpp @@ -73,36 +73,19 @@ MTLClearColor MTLClearColorFromARGB (uint32_t argb) //============================================================================== -class LowLevelRenderContextMetal : public GraphicsContext +class GraphicsContextMetal : public GraphicsContext { public: //============================================================================== - LowLevelRenderContextMetal (Options fiddleOptions, GpuDevice::Ptr existingGpu = {}) - : m_fiddleOptions (fiddleOptions) + GraphicsContextMetal (Options options, GpuDevice::Ptr existingGpu = {}) + : options (options) { // Obtain or create the GpuDevice if (existingGpu != nullptr) - { - m_gpuContext = std::move (existingGpu); - } + gpuDevice = std::move (existingGpu); else - { - m_gpuContext = GpuDevice::create (GpuPlatform::Metal, fiddleOptions); - } - - // Own GpuDeviceMetal knows the native device/queue — extract them. - // GpuDeviceMetal exposes getDevice()/getCommandQueue() for sharing. - jassert (m_gpuContext != nullptr); - - // Create the Rive render context (needed for windowed rendering + vector content) - if (m_fiddleOptions.synchronousShaderCompilations) - m_renderContextOptions.shaderCompilationMode = rive::gpu::ShaderCompilationMode::alwaysSynchronous; - - if (m_fiddleOptions.disableRasterOrdering) - m_renderContextOptions.disableFramebufferReads = true; - - m_renderContext = rive::gpu::RenderContextMetalImpl::MakeContext (m_gpu, m_renderContextOptions); + gpuDevice = GpuDevice::create (GpuPlatform::Metal, options); // Compile PLS shaders for the fullscreen blit pipeline NSError* error = nil; @@ -113,7 +96,7 @@ class LowLevelRenderContextMetal : public GraphicsContext nil, nil); - auto* plsPrecompiledLibrary = [m_gpu newLibraryWithData:metallibData error:&error]; + auto* plsPrecompiledLibrary = [gpu newLibraryWithData:metallibData error:&error]; if (plsPrecompiledLibrary == nil || error != nil) { NSLog (@"Failed to load binary shaders: %@", error); @@ -139,8 +122,8 @@ class LowLevelRenderContextMetal : public GraphicsContext pipelineDescriptor.vertexDescriptor = vertexDescriptor; pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; - m_pipelineState = [m_gpu newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; - if (m_pipelineState == nil || error != nil) + pipelineState = [gpu newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; + if (pipelineState == nil || error != nil) { NSLog (@"Failed to create pipeline state: %@", error); @@ -148,22 +131,22 @@ class LowLevelRenderContextMetal : public GraphicsContext return; } - m_quadVertexBuffer = [m_gpu newBufferWithBytes:quadVertices length:sizeof (quadVertices) options:MTLResourceStorageModeShared]; + quadVertexBuffer = [gpu newBufferWithBytes:quadVertices length:sizeof (quadVertices) options:MTLResourceStorageModeShared]; } //============================================================================== GpuPlatform getPlatform() const noexcept override { return GpuPlatform::Metal; } - GpuDevice::Ptr getGpuDevice() const noexcept override { return m_gpuContext; } + GpuDevice::Ptr getGpuDevice() const noexcept override { return gpuDevice; } //============================================================================== - rive::Factory* factory() override { return m_renderContext.get(); } + rive::Factory* getFactory() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderContext* renderContext() override { return m_renderContext.get(); } + rive::gpu::RenderContext* getRenderContext() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderTarget* renderTarget() override { return m_renderTarget.get(); } + rive::gpu::RenderTarget* getRenderTarget() override { return renderTarget.get(); } //============================================================================== @@ -174,72 +157,72 @@ class LowLevelRenderContextMetal : public GraphicsContext NSView* nsView = [nsWindow contentView]; #endif - if (m_swapchain == nil) + if (swapchain == nil) { #if YUP_MAC nsView.wantsLayer = YES; #endif - m_swapchain = [CAMetalLayer layer]; - m_swapchain.device = m_gpu; - m_swapchain.opaque = YES; - m_swapchain.framebufferOnly = ! m_fiddleOptions.readableFramebuffer; - m_swapchain.pixelFormat = MTLPixelFormatBGRA8Unorm; + swapchain = [CAMetalLayer layer]; + swapchain.device = gpu; + swapchain.opaque = YES; + swapchain.framebufferOnly = ! options.readableFramebuffer; + swapchain.pixelFormat = MTLPixelFormatBGRA8Unorm; #if YUP_MAC - m_swapchain.displaySyncEnabled = NO; + swapchain.displaySyncEnabled = NO; #endif #if YUP_IOS UIView* view = (__bridge UIView*) window; - m_swapchain.frame = view.bounds; - [view.layer addSublayer:m_swapchain]; + swapchain.frame = view.bounds; + [view.layer addSublayer:swapchain]; #else - nsView.layer = m_swapchain; + nsView.layer = swapchain; #endif } - m_swapchain.contentsScale = dpiScale; - m_swapchain.drawableSize = CGSizeMake (width, height); + swapchain.contentsScale = dpiScale; + swapchain.drawableSize = CGSizeMake (width, height); - auto renderContextImpl = m_renderContext->static_impl_cast(); - m_renderTarget = renderContextImpl->makeRenderTarget (MTLPixelFormatBGRA8Unorm, width, height); + auto renderContextImpl = getRenderContext()->static_impl_cast(); + renderTarget = renderContextImpl->makeRenderTarget (MTLPixelFormatBGRA8Unorm, width, height); - if (m_currentTexture != nil) - m_currentTexture = nil; + if (currentTexture != nil) + currentTexture = nil; MTLTextureDescriptor* descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:(MTLPixelFormatBGRA8Unorm) width:width height:height mipmapped:NO]; descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; - m_currentTexture = [m_gpu newTextureWithDescriptor:descriptor]; + currentTexture = [gpu newTextureWithDescriptor:descriptor]; } //============================================================================== std::unique_ptr makeRenderer (int width, int height) override { - return std::make_unique (m_renderContext.get()); + return std::make_unique (getRenderContext()); } //============================================================================== void begin (const rive::gpu::RenderContext::FrameDescriptor& frameDescriptor) override { - m_renderContext->beginFrame (frameDescriptor); + getRenderContext()->beginFrame (frameDescriptor); if (frameDescriptor.loadAction == rive::gpu::LoadAction::clear) { - id presentCommandBuffer = [m_queue commandBuffer]; + id presentCommandBuffer = [queue commandBuffer]; MTLRenderPassDescriptor* passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - passDescriptor.colorAttachments[0].texture = m_currentTexture; + passDescriptor.colorAttachments[0].texture = currentTexture; passDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; passDescriptor.colorAttachments[0].clearColor = MTLClearColorFromARGB (frameDescriptor.clearColor); passDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; id encoder = [presentCommandBuffer renderCommandEncoderWithDescriptor:passDescriptor]; - [encoder setRenderPipelineState:m_pipelineState]; + [encoder setRenderPipelineState:pipelineState]; [encoder endEncoding]; [presentCommandBuffer commit]; @@ -248,62 +231,61 @@ class LowLevelRenderContextMetal : public GraphicsContext void end (void*) override { - jassert (m_renderTarget != nil); + jassert (renderTarget != nil); // Render into texture - jassert (m_currentTexture.width == m_renderTarget->width()); - jassert (m_currentTexture.height == m_renderTarget->height()); - m_renderTarget->setTargetTexture (m_currentTexture); + jassert (currentTexture.width == renderTarget->width()); + jassert (currentTexture.height == renderTarget->height()); + renderTarget->setTargetTexture (currentTexture); - id presentCommandBuffer = [m_queue commandBuffer]; - m_renderContext->flush ({ .renderTarget = m_renderTarget.get(), .externalCommandBuffer = (__bridge void*) presentCommandBuffer }); + id presentCommandBuffer = [queue commandBuffer]; + getRenderContext()->flush ({ .renderTarget = renderTarget.get(), .externalCommandBuffer = (__bridge void*) presentCommandBuffer }); // Render texture in view drawable - jassert (m_currentFrameSurface == nil); - m_currentFrameSurface = [m_swapchain nextDrawable]; - jassert (m_currentFrameSurface.texture.width == m_renderTarget->width()); - jassert (m_currentFrameSurface.texture.height == m_renderTarget->height()); + jassert (currentFrameSurface == nil); + currentFrameSurface = [swapchain nextDrawable]; + jassert (currentFrameSurface.texture.width == renderTarget->width()); + jassert (currentFrameSurface.texture.height == renderTarget->height()); MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - renderPassDescriptor.colorAttachments[0].texture = m_currentFrameSurface.texture; + renderPassDescriptor.colorAttachments[0].texture = currentFrameSurface.texture; renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionDontCare; renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; id renderEncoder = [presentCommandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; - [renderEncoder setRenderPipelineState:m_pipelineState]; - [renderEncoder setFragmentTexture:m_currentTexture atIndex:0]; - [renderEncoder setVertexBuffer:m_quadVertexBuffer offset:0 atIndex:0]; + [renderEncoder setRenderPipelineState:pipelineState]; + [renderEncoder setFragmentTexture:currentTexture atIndex:0]; + [renderEncoder setVertexBuffer:quadVertexBuffer offset:0 atIndex:0]; [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; [renderEncoder endEncoding]; - [presentCommandBuffer presentDrawable:m_currentFrameSurface]; + [presentCommandBuffer presentDrawable:currentFrameSurface]; [presentCommandBuffer commit]; - m_currentFrameSurface = nil; - m_renderTarget->setTargetTexture (nil); + currentFrameSurface = nil; + renderTarget->setTargetTexture (nil); } private: - const Options m_fiddleOptions; - rive::gpu::RenderContextMetalImpl::ContextOptions m_renderContextOptions; - GpuDevice::Ptr m_gpuContext; - std::unique_ptr m_renderContext; - id m_gpu = MTLCreateSystemDefaultDevice(); - id m_queue = [m_gpu newCommandQueue]; - CAMetalLayer* m_swapchain = nil; - rive::rcp m_renderTarget; - id m_currentFrameSurface = nil; - id m_pipelineState = nil; - id m_currentTexture = nil; - id m_quadVertexBuffer = nil; + const Options options; + rive::gpu::RenderContextMetalImpl::ContextOptions renderContextOptions; + GpuDevice::Ptr gpuDevice; + id gpu = MTLCreateSystemDefaultDevice(); + id queue = [gpu newCommandQueue]; + CAMetalLayer* swapchain = nil; + rive::rcp renderTarget; + id currentFrameSurface = nil; + id pipelineState = nil; + id currentTexture = nil; + id quadVertexBuffer = nil; }; //============================================================================== -std::unique_ptr yup_constructMetalGraphicsContext (GpuDevice::Options fiddleOptions, +std::unique_ptr yup_constructMetalGraphicsContext (GpuDevice::Options options, GpuDevice::Ptr existingGpu) { - return std::make_unique (fiddleOptions, std::move (existingGpu)); + return std::make_unique (options, std::move (existingGpu)); } } // namespace yup diff --git a/modules/yup_graphics/native/yup_GraphicsContext_opengl.cpp b/modules/yup_graphics/native/yup_GraphicsContext_opengl.cpp index b74933c08..7dd384e00 100644 --- a/modules/yup_graphics/native/yup_GraphicsContext_opengl.cpp +++ b/modules/yup_graphics/native/yup_GraphicsContext_opengl.cpp @@ -33,90 +33,22 @@ namespace yup { -#if RIVE_DESKTOP_GL && DEBUG -static void GLAPIENTRY err_msg_callback (GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam) -{ - if (type == GL_DEBUG_TYPE_ERROR_KHR) - { - printf ("GL ERROR: %s\n", message); - fflush (stdout); - assert (false); - } - else if (type == GL_DEBUG_TYPE_PERFORMANCE_KHR) - { - if (strcmp (message, "API_ID_REDUNDANT_FBO performance warning has been generated. Redundant state " - "change in glBindFramebuffer API call, FBO 0, \"\", already bound.") - == 0) - return; - if (strstr (message, "is being recompiled based on GL state.")) - return; - printf ("GL PERF: %s\n", message); - fflush (stdout); - } -} -#endif - //============================================================================== -class LowLevelRenderContextGL : public GraphicsContext +class GraphicsContextOpenGL : public GraphicsContext { public: - LowLevelRenderContextGL (Options options, GpuDevice::Ptr existingGpu = {}) - : m_options (options) + GraphicsContextOpenGL (Options options, GpuDevice::Ptr existingGpu = {}) + : options (options) { -#if RIVE_DESKTOP_GL - if (! gladLoadCustomLoader ((GLADloadfunc) options.loaderFunction)) - { - fprintf (stderr, "Failed to initialize glad.\n"); - return; - } -#endif - // Obtain or create the GpuDevice for offscreen/RHI operations if (existingGpu != nullptr) - m_gpuContext = std::move (existingGpu); + gpuDevice = std::move (existingGpu); else - m_gpuContext = GpuDevice::create (getPlatform(), options); - - // Create the main window render context - m_renderContext = rive::gpu::RenderContextGLImpl::MakeContext (m_renderContextOptions); - if (! m_renderContext) - { - fprintf (stderr, "Failed to create a renderer.\n"); - return; - } - - printf ("GL_VENDOR: %s\n", glGetString (GL_VENDOR)); - printf ("GL_RENDERER: %s\n", glGetString (GL_RENDERER)); - printf ("GL_VERSION: %s\n", glGetString (GL_VERSION)); - -#if RIVE_DESKTOP_GL - printf ("GL_ANGLE_shader_pixel_local_storage_coherent: %i\n", GLAD_GL_ANGLE_shader_pixel_local_storage_coherent); -#if DEBUG - if (GLAD_GL_KHR_debug) - { - glEnable (GL_DEBUG_OUTPUT_KHR); - glDebugMessageControlKHR (GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); - glDebugMessageCallbackKHR (&err_msg_callback, nullptr); - } -#endif -#endif - -#if DEBUG && ! RIVE_ANDROID - int n; - glGetIntegerv (GL_NUM_EXTENSIONS, &n); - for (size_t i = 0; i < n; ++i) - printf (" %s\n", glGetStringi (GL_EXTENSIONS, i)); -#endif + gpuDevice = GpuDevice::create (getPlatform(), options); } - ~LowLevelRenderContextGL() + ~GraphicsContextOpenGL() { cleanupOffscreenResources(); } @@ -130,55 +62,55 @@ class LowLevelRenderContextGL : public GraphicsContext #endif } - GpuDevice::Ptr getGpuDevice() const noexcept override { return m_gpuContext; } + GpuDevice::Ptr getGpuDevice() const noexcept override { return gpuDevice; } - rive::Factory* factory() override { return m_renderContext.get(); } + rive::Factory* getFactory() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderContext* renderContext() override { return m_renderContext.get(); } + rive::gpu::RenderContext* getRenderContext() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderTarget* renderTarget() override { return m_offscreenRenderTarget.get(); } + rive::gpu::RenderTarget* getRenderTarget() override { return offscreenRenderTarget.get(); } - void onSizeChanged (void* window, int width, int height, float dpiScale, uint32_t sampleCount) override + void onSizeChanged (void* window, int newWidth, int newHeight, float dpiScale, uint32_t newSampleCount) override { - m_width = width; - m_height = height; - m_sampleCount = sampleCount; + width = newWidth; + height = newHeight; + sampleCount = newSampleCount; createOffscreenResources(); } std::unique_ptr makeRenderer (int width, int height) override { - return std::make_unique (m_renderContext.get()); + return std::make_unique (getRenderContext()); } void begin (const rive::gpu::RenderContext::FrameDescriptor& frameDescriptor) override { - m_renderContext->static_impl_cast()->invalidateGLState(); - m_renderContext->beginFrame (frameDescriptor); + getRenderContext()->static_impl_cast()->invalidateGLState(); + getRenderContext()->beginFrame (frameDescriptor); } void end (void*) override { - m_renderContext->static_impl_cast()->invalidateGLState(); - m_renderContext->flush ({ m_offscreenRenderTarget.get() }); - m_renderContext->static_impl_cast()->unbindGLInternalResources(); + getRenderContext()->static_impl_cast()->invalidateGLState(); + getRenderContext()->flush ({ offscreenRenderTarget.get() }); + getRenderContext()->static_impl_cast()->unbindGLInternalResources(); blitToMainFramebuffer(); } private: void createOffscreenResources() { - if (m_width <= 0 || m_height <= 0) + if (width <= 0 || height <= 0) { - fprintf (stderr, "createOffscreenResources: Invalid size %dx%d\n", m_width, m_height); + fprintf (stderr, "createOffscreenResources: Invalid size %dx%d\n", width, height); return; } cleanupOffscreenResources(); - glGenTextures (1, &m_offscreenTexture); - glBindTexture (GL_TEXTURE_2D, m_offscreenTexture); - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glGenTextures (1, &offscreenTexture); + glBindTexture (GL_TEXTURE_2D, offscreenTexture); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -190,9 +122,9 @@ class LowLevelRenderContextGL : public GraphicsContext glBindTexture (GL_TEXTURE_2D, 0); - glGenFramebuffers (1, &m_offscreenFramebuffer); - glBindFramebuffer (GL_FRAMEBUFFER, m_offscreenFramebuffer); - glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_offscreenTexture, 0); + glGenFramebuffers (1, &offscreenFramebuffer); + glBindFramebuffer (GL_FRAMEBUFFER, offscreenFramebuffer); + glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, offscreenTexture, 0); GLenum status = glCheckFramebufferStatus (GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) @@ -200,55 +132,53 @@ class LowLevelRenderContextGL : public GraphicsContext glBindFramebuffer (GL_FRAMEBUFFER, 0); - m_offscreenRenderTarget = rive::make_rcp ( - m_width, m_height, m_offscreenFramebuffer, m_sampleCount); + offscreenRenderTarget = rive::make_rcp ( + width, height, offscreenFramebuffer, sampleCount); } void cleanupOffscreenResources() { - if (m_offscreenFramebuffer != 0) + if (offscreenFramebuffer != 0) { - glDeleteFramebuffers (1, &m_offscreenFramebuffer); - m_offscreenFramebuffer = 0; + glDeleteFramebuffers (1, &offscreenFramebuffer); + offscreenFramebuffer = 0; } - if (m_offscreenTexture != 0) + if (offscreenTexture != 0) { - glDeleteTextures (1, &m_offscreenTexture); - m_offscreenTexture = 0; + glDeleteTextures (1, &offscreenTexture); + offscreenTexture = 0; } - m_offscreenRenderTarget.reset(); + offscreenRenderTarget.reset(); } void blitToMainFramebuffer() { - if (m_offscreenTexture == 0) + if (offscreenTexture == 0) { fprintf (stderr, "blitToMainFramebuffer: Invalid program or texture\n"); return; } - glBindFramebuffer (GL_READ_FRAMEBUFFER, m_offscreenFramebuffer); + glBindFramebuffer (GL_READ_FRAMEBUFFER, offscreenFramebuffer); glBindFramebuffer (GL_DRAW_FRAMEBUFFER, 0); - glBlitFramebuffer (0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBlitFramebuffer (0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); } - Options m_options; - rive::gpu::RenderContextGLImpl::ContextOptions m_renderContextOptions; - GpuDevice::Ptr m_gpuContext; - std::unique_ptr m_renderContext; - rive::rcp m_offscreenRenderTarget; - - GLuint m_offscreenFramebuffer = 0; - GLuint m_offscreenTexture = 0; - int m_width = 0; - int m_height = 0; - uint32_t m_sampleCount = 0; + Options options; + GpuDevice::Ptr gpuDevice; + rive::rcp offscreenRenderTarget; + + GLuint offscreenFramebuffer = 0; + GLuint offscreenTexture = 0; + int width = 0; + int height = 0; + uint32_t sampleCount = 0; }; //============================================================================== std::unique_ptr yup_constructOpenGLGraphicsContext (GpuDevice::Options options, GpuDevice::Ptr existingGpu) { - return std::make_unique (options, std::move (existingGpu)); + return std::make_unique (options, std::move (existingGpu)); } } // namespace yup diff --git a/modules/yup_graphics/native/yup_GraphicsContext_webgpu.cpp b/modules/yup_graphics/native/yup_GraphicsContext_webgpu.cpp index c6fdce901..cdb210cdc 100644 --- a/modules/yup_graphics/native/yup_GraphicsContext_webgpu.cpp +++ b/modules/yup_graphics/native/yup_GraphicsContext_webgpu.cpp @@ -35,62 +35,53 @@ namespace yup { -class LowLevelRenderContextWebGPU : public GraphicsContext +class GraphicsContextWebGPU : public GraphicsContext { public: - LowLevelRenderContextWebGPU (Options options, GpuDevice::Ptr existingGpu = {}) - : m_options (options) + GraphicsContextWebGPU (Options options, GpuDevice::Ptr existingGpu = {}) + : options (options) { - m_device = wgpu::Device::Acquire (emscripten_webgpu_get_device()); - if (m_device == nullptr) + device = wgpu::Device::Acquire (emscripten_webgpu_get_device()); + if (device == nullptr) { jassertfalse; fprintf (stderr, "WebGPU: no device. Ensure Module.preinitializedWebGPUDevice is set before main().\n"); return; } - m_queue = m_device.GetQueue(); + queue = device.GetQueue(); // Obtain or create the GpuDevice for RHI/offscreen operations if (existingGpu != nullptr) - m_gpuContext = std::move (existingGpu); + gpuDevice = std::move (existingGpu); else - m_gpuContext = GpuDevice::create (GpuPlatform::WebGPU, options); - - m_renderContext = rive::gpu::RenderContextWebGPUImpl::MakeContext ( - {}, m_device, m_queue, rive::gpu::RenderContextWebGPUImpl::ContextOptions()); - - if (m_renderContext == nullptr) - { - fprintf (stderr, "WebGPU: failed to create a render context.\n"); - return; - } + gpuDevice = GpuDevice::create (GpuPlatform::WebGPU, options); } GpuPlatform getPlatform() const noexcept override { return GpuPlatform::WebGPU; } - GpuDevice::Ptr getGpuDevice() const noexcept override { return m_gpuContext; } + GpuDevice::Ptr getGpuDevice() const noexcept override { return gpuDevice; } - rive::Factory* factory() override { return m_renderContext.get(); } + rive::Factory* getFactory() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderContext* renderContext() override { return m_renderContext.get(); } + rive::gpu::RenderContext* getRenderContext() override { return gpuDevice->getRenderContext(); } - rive::gpu::RenderTarget* renderTarget() override { return m_renderTarget.get(); } + rive::gpu::RenderTarget* getRenderTarget() override { return renderTarget.get(); } std::unique_ptr makeRenderer (int width, int height) override { - return std::make_unique (m_renderContext.get()); + return std::make_unique (getRenderContext()); } - void onSizeChanged (void*, int width, int height, float dpiScale, uint32_t) override + void onSizeChanged (void*, int newWidth, int newHeight, float dpiScale, uint32_t) override { - if (m_renderContext == nullptr || width <= 0 || height <= 0) + if (gpuDevice == nullptr || getRenderContext() == nullptr || newWidth <= 0 || newHeight <= 0) return; - m_width = width; - m_height = height; + width = newWidth; + height = newHeight; - if (m_surface == nullptr) + if (surface == nullptr) { wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc = {}; canvasDesc.selector = "#canvas"; @@ -99,11 +90,11 @@ class LowLevelRenderContextWebGPU : public GraphicsContext surfaceDesc.nextInChain = &canvasDesc; wgpu::Instance instance = wgpu::CreateInstance(); - m_surface = instance.CreateSurface (&surfaceDesc); + surface = instance.CreateSurface (&surfaceDesc); } wgpu::SurfaceConfiguration config = {}; - config.device = m_device; + config.device = device; config.format = wgpu::TextureFormat::BGRA8Unorm; config.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst; config.width = (uint32_t) width; @@ -111,7 +102,7 @@ class LowLevelRenderContextWebGPU : public GraphicsContext config.alphaMode = wgpu::CompositeAlphaMode::Auto; config.presentMode = wgpu::PresentMode::Fifo; - m_surface.Configure (&config); + surface.Configure (&config); wgpu::TextureDescriptor textureDesc = {}; textureDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc; @@ -119,39 +110,38 @@ class LowLevelRenderContextWebGPU : public GraphicsContext textureDesc.size = { (uint32_t) width, (uint32_t) height, 1 }; textureDesc.format = wgpu::TextureFormat::BGRA8Unorm; - m_offscreenTexture = m_device.CreateTexture (&textureDesc); - m_offscreenTextureView = m_offscreenTexture.CreateView(); + offscreenTexture = device.CreateTexture (&textureDesc); + offscreenTextureView = offscreenTexture.CreateView(); - m_renderTarget = m_renderContext->static_impl_cast() - ->makeRenderTarget (wgpu::TextureFormat::BGRA8Unorm, (uint32_t) width, (uint32_t) height); + renderTarget = getRenderContext()->static_impl_cast()->makeRenderTarget (wgpu::TextureFormat::BGRA8Unorm, (uint32_t) width, (uint32_t) height); } void begin (const rive::gpu::RenderContext::FrameDescriptor& frameDescriptor) override { - if (m_offscreenTextureView == nullptr || m_renderTarget == nullptr) + if (offscreenTextureView == nullptr || renderTarget == nullptr) return; - m_renderTarget->setTargetTextureView (m_offscreenTextureView, m_offscreenTexture); - m_renderContext->beginFrame (frameDescriptor); + renderTarget->setTargetTextureView (offscreenTextureView, offscreenTexture); + getRenderContext()->beginFrame (frameDescriptor); } void end (void*) override { - if (m_renderTarget == nullptr || m_offscreenTexture == nullptr || m_surface == nullptr) + if (renderTarget == nullptr || offscreenTexture == nullptr || surface == nullptr) return; wgpu::SurfaceTexture surfaceTexture = {}; - m_surface.GetCurrentTexture (&surfaceTexture); + surface.GetCurrentTexture (&surfaceTexture); if (surfaceTexture.texture == nullptr) return; - wgpu::CommandEncoder encoder = m_device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - m_renderContext->flush ({ .renderTarget = m_renderTarget.get(), - .externalCommandBuffer = encoder.Get() }); + getRenderContext()->flush ({ .renderTarget = renderTarget.get(), + .externalCommandBuffer = encoder.Get() }); wgpu::TexelCopyTextureInfo copySource = {}; - copySource.texture = m_offscreenTexture; + copySource.texture = offscreenTexture; copySource.aspect = wgpu::TextureAspect::All; wgpu::TexelCopyTextureInfo copyDestination = {}; @@ -159,35 +149,34 @@ class LowLevelRenderContextWebGPU : public GraphicsContext copyDestination.aspect = wgpu::TextureAspect::All; wgpu::Extent3D copySize = {}; - copySize.width = (uint32_t) m_width; - copySize.height = (uint32_t) m_height; + copySize.width = (uint32_t) width; + copySize.height = (uint32_t) height; copySize.depthOrArrayLayers = 1; encoder.CopyTextureToTexture (©Source, ©Destination, ©Size); wgpu::CommandBuffer commands = encoder.Finish(); - m_queue.Submit (1, &commands); + queue.Submit (1, &commands); - m_renderTarget->setTargetTextureView ({}, {}); + renderTarget->setTargetTextureView ({}, {}); } private: - Options m_options; - GpuDevice::Ptr m_gpuContext; - wgpu::Device m_device; - wgpu::Queue m_queue; - wgpu::Surface m_surface; - wgpu::Texture m_offscreenTexture; - wgpu::TextureView m_offscreenTextureView; - int m_width = 0; - int m_height = 0; - std::unique_ptr m_renderContext; - rive::rcp m_renderTarget; + Options options; + GpuDevice::Ptr gpuDevice; + wgpu::Device device; + wgpu::Queue queue; + wgpu::Surface surface; + wgpu::Texture offscreenTexture; + wgpu::TextureView offscreenTextureView; + int width = 0; + int height = 0; + rive::rcp renderTarget; }; std::unique_ptr yup_constructWebGPUGraphicsContext (GpuDevice::Options options, GpuDevice::Ptr existingGpu) { - return std::make_unique (options, std::move (existingGpu)); + return std::make_unique (options, std::move (existingGpu)); } } // namespace yup diff --git a/modules/yup_gui/native/yup_Initialisation_sdl.cpp b/modules/yup_gui/native/yup_Initialisation_sdl.cpp index 9b06f4803..c14bf663f 100644 --- a/modules/yup_gui/native/yup_Initialisation_sdl.cpp +++ b/modules/yup_gui/native/yup_Initialisation_sdl.cpp @@ -77,7 +77,11 @@ bool displayEventDispatcher (void* userdata, SDL_Event* event) { float x = 0.0f, y = 0.0f; SDL_GetGlobalMouseState (&x, &y); - auto cursorPosition = Point { x, y }; + + const SDL_Point pt { static_cast (x), static_cast (y) }; + const auto displayScale = getDisplayUnitsPerPoint (SDL_GetDisplayForPoint (&pt)); + + auto cursorPosition = Point { x / displayScale, y / displayScale }; auto keyModifiers = toKeyModifiers (SDL_GetModState()); MouseEvent mouseEvent ( @@ -98,7 +102,11 @@ bool displayEventDispatcher (void* userdata, SDL_Event* event) { float x = 0.0f, y = 0.0f; SDL_GetGlobalMouseState (&x, &y); - auto cursorPosition = Point { x, y }; + + const SDL_Point pt { static_cast (x), static_cast (y) }; + const auto displayScale = getDisplayUnitsPerPoint (SDL_GetDisplayForPoint (&pt)); + + auto cursorPosition = Point { x / displayScale, y / displayScale }; auto button = toMouseButton (event->button.button); auto keyModifiers = toKeyModifiers (SDL_GetModState()); @@ -115,7 +123,11 @@ bool displayEventDispatcher (void* userdata, SDL_Event* event) { float x = 0.0f, y = 0.0f; SDL_GetGlobalMouseState (&x, &y); - auto cursorPosition = Point { x, y }; + + const SDL_Point pt { static_cast (x), static_cast (y) }; + const auto displayScale = getDisplayUnitsPerPoint (SDL_GetDisplayForPoint (&pt)); + + auto cursorPosition = Point { x / displayScale, y / displayScale }; auto button = toMouseButton (event->button.button); auto keyModifiers = toKeyModifiers (SDL_GetModState()); @@ -132,7 +144,11 @@ bool displayEventDispatcher (void* userdata, SDL_Event* event) { float x = 0.0f, y = 0.0f; SDL_GetGlobalMouseState (&x, &y); - auto cursorPosition = Point { x, y }; + + const SDL_Point pt { static_cast (x), static_cast (y) }; + const auto displayScale = getDisplayUnitsPerPoint (SDL_GetDisplayForPoint (&pt)); + + auto cursorPosition = Point { x / displayScale, y / displayScale }; auto keyModifiers = toKeyModifiers (SDL_GetModState()); auto mouseWheelData = MouseWheelData { static_cast (event->wheel.x), static_cast (event->wheel.y) }; diff --git a/modules/yup_gui/native/yup_Windowing_sdl.cpp b/modules/yup_gui/native/yup_Windowing_sdl.cpp index e8d154eb8..70eb48f92 100644 --- a/modules/yup_gui/native/yup_Windowing_sdl.cpp +++ b/modules/yup_gui/native/yup_Windowing_sdl.cpp @@ -382,11 +382,15 @@ void SDLComponentNative::setPosition (const Point& newPosition) Point SDLComponentNative::getPosition() const { int x = 0, y = 0; + float scale = 0.0f; +#if ! (YUP_MOBILE || YUP_EMSCRIPTEN) if (window != nullptr) SDL_GetWindowPosition (window, &x, &y); - const auto scale = getWindowUnitsPerPoint (window); + scale = getWindowUnitsPerPoint (window); +#endif + return { roundToInt (x / scale), roundToInt (y / scale) }; } @@ -719,7 +723,7 @@ Point SDLComponentNative::getCursorPosition() const rive::Factory* SDLComponentNative::getFactory() { - return context ? context->factory() : nullptr; + return context ? context->getFactory() : nullptr; } //============================================================================== @@ -836,7 +840,7 @@ void SDLComponentNative::handleAsyncUpdate() if (! isThreadRunning() || ! isInitialised.test_and_set()) return; - renderContext(); + getRenderContext(); renderEvent.signal(); } @@ -863,12 +867,12 @@ void SDLComponentNative::timerCallback() pollCapturedMouseState(); #endif - renderContext(); + getRenderContext(); } //============================================================================== -void SDLComponentNative::renderContext() +void SDLComponentNative::getRenderContext() { YUP_PROFILE_NAMED_INTERNAL_TRACE (RenderContext); diff --git a/modules/yup_gui/native/yup_Windowing_sdl.h b/modules/yup_gui/native/yup_Windowing_sdl.h index 9971efe30..6a6568d3f 100644 --- a/modules/yup_gui/native/yup_Windowing_sdl.h +++ b/modules/yup_gui/native/yup_Windowing_sdl.h @@ -175,7 +175,7 @@ class SDLComponentNative final Component* findComponentForMouseEvent (const Point& position); void updateComponentUnderMouse (const MouseEvent& event); - void renderContext(); + void getRenderContext(); void startRendering(); void stopRendering(); diff --git a/modules/yup_rhi/context/yup_GpuDevice.cpp b/modules/yup_rhi/context/yup_GpuDevice.cpp index 701ec1a86..e4ada70a2 100644 --- a/modules/yup_rhi/context/yup_GpuDevice.cpp +++ b/modules/yup_rhi/context/yup_GpuDevice.cpp @@ -22,7 +22,8 @@ namespace yup { -// Forward declarations for backend-specific factory functions +//============================================================================== + std::unique_ptr yup_constructHeadlessGpuDevice (GpuDevice::Options); #if YUP_RIVE_USE_METAL && (YUP_MAC || YUP_IOS) std::unique_ptr yup_constructMetalGpuDevice (GpuDevice::Options); @@ -39,6 +40,8 @@ std::unique_ptr yup_constructWebGPUGpuDevice (GpuDevice::Options); std::unique_ptr yup_constructDawnGpuDevice (GpuDevice::Options); #endif +//============================================================================== + GpuDevice::Ptr GpuDevice::create (GpuPlatform gpuApi, Options options) { std::unique_ptr ctx; @@ -98,7 +101,6 @@ ReferenceCountedObjectPtr GpuDevice::createBuffer (GpuBufferType type const void* data, size_t byteSize) { - jassert (data != nullptr && byteSize > 0); if (data == nullptr || byteSize == 0) return nullptr; @@ -106,7 +108,7 @@ ReferenceCountedObjectPtr GpuDevice::createBuffer (GpuBufferType type if (type == GpuBufferType::storage) return nullptr; - auto* oreCtx = gpuContext(); + auto* oreCtx = getGpuContext(); if (oreCtx == nullptr) return nullptr; diff --git a/modules/yup_rhi/context/yup_GpuDevice.h b/modules/yup_rhi/context/yup_GpuDevice.h index 0e68d634c..dc124a4b0 100644 --- a/modules/yup_rhi/context/yup_GpuDevice.h +++ b/modules/yup_rhi/context/yup_GpuDevice.h @@ -58,7 +58,6 @@ class YUP_API GpuDevice : public ReferenceCountedObject bool retinaDisplay = true; ///< Whether the context supports Retina or high-DPI displays. bool readableFramebuffer = false; ///< Allows the framebuffer to be readable. bool synchronousShaderCompilations = false; ///< Controls whether shader compilations are done synchronously. - bool enableReadPixels = false; ///< Enables reading pixels directly from the framebuffer. bool disableRasterOrdering = false; ///< Disables specific raster ordering features for performance. bool allowHeadlessRendering = false; ///< Allows rendering without a visible window (headless mode). LoaderFunction loaderFunction = nullptr; ///< Loader function (used by GL/Vulkan). @@ -92,21 +91,33 @@ class YUP_API GpuDevice : public ReferenceCountedObject virtual GpuPlatform getPlatform() const noexcept = 0; //============================================================================== + /** Returns the backend-specific GPU render context, or nullptr if unavailable. + + This is the native GPU context used by the Rive renderer. It may be + nullptr on backends that do not support rendering (e.g., headless compute + or OpenGL without a window). + + @return A pointer to the backend-specific RenderContext, or nullptr if unavailable. + */ + virtual rive::gpu::RenderContext* getRenderContext() const { return nullptr; } + /** Returns the backend-agnostic ore GPU context, or nullptr when ore is unavailable on this backend. This is the single backend bridge used by the RHI layer (GpuPipeline, GpuFrame, GpuRenderPass, GpuBuffer). User code should prefer the dependency-free isGpuAvailable() capability probe instead. + + @return A pointer to the ore::Context, or nullptr if unavailable. */ - virtual rive::ore::Context* gpuContext() const noexcept { return nullptr; } + virtual rive::ore::Context* getGpuContext() const noexcept { return nullptr; } /** Returns true if a GPU (ore) context is available for RHI operations. - Equivalent to gpuContext() != nullptr but without referencing any ore + Equivalent to getGpuContext() != nullptr but without referencing any ore type, so user code and examples can probe GPU capability ore-free. */ - bool isGpuAvailable() const noexcept { return gpuContext() != nullptr; } + bool isGpuAvailable() const noexcept { return getGpuContext() != nullptr; } /** Returns true if compute shaders are available on this backend. diff --git a/modules/yup_rhi/native/yup_GpuComputePipeline_d3d.cpp b/modules/yup_rhi/native/yup_GpuComputePipeline_d3d.cpp index 7b83e09f8..ce3ce8143 100644 --- a/modules/yup_rhi/native/yup_GpuComputePipeline_d3d.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePipeline_d3d.cpp @@ -31,17 +31,17 @@ class GpuComputePipelineD3D11 final : public GpuComputePipeline public: GpuComputePipelineD3D11 (ComPtr shader, GpuWorkgroupSize wgs) : computeShader (std::move (shader)) - , workgroupSize_ (wgs) + , workgroupSize (wgs) { } - GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize_; } + GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize; } ID3D11ComputeShader* getComputeShader() const noexcept { return computeShader.Get(); } private: ComPtr computeShader; - GpuWorkgroupSize workgroupSize_; + GpuWorkgroupSize workgroupSize; }; //============================================================================== diff --git a/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp b/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp index 00e2efee3..ddc93fadc 100644 --- a/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePipeline_metal.cpp @@ -31,17 +31,17 @@ class GpuComputePipelineMetal final : public GpuComputePipeline public: GpuComputePipelineMetal (id state, GpuWorkgroupSize wgs) : pipelineState (state) - , workgroupSize_ (wgs) + , workgroupSize (wgs) { } - GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize_; } + GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize; } id getPipelineState() const noexcept { return pipelineState; } private: id pipelineState; - GpuWorkgroupSize workgroupSize_; + GpuWorkgroupSize workgroupSize; }; //============================================================================== diff --git a/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp b/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp index a9d69877b..9f826ba81 100644 --- a/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePipeline_opengl.cpp @@ -31,7 +31,7 @@ class GpuComputePipelineGL final : public GpuComputePipeline public: GpuComputePipelineGL (GLuint program, GpuWorkgroupSize wgs) : glProgram (program) - , workgroupSize_ (wgs) + , workgroupSize (wgs) { } @@ -41,13 +41,13 @@ class GpuComputePipelineGL final : public GpuComputePipeline glDeleteProgram (glProgram); } - GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize_; } + GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize; } GLuint getProgram() const noexcept { return glProgram; } private: GLuint glProgram; - GpuWorkgroupSize workgroupSize_; + GpuWorkgroupSize workgroupSize; }; //============================================================================== diff --git a/modules/yup_rhi/native/yup_GpuComputePipeline_webgpu.cpp b/modules/yup_rhi/native/yup_GpuComputePipeline_webgpu.cpp index da18a2316..3c75feb1d 100644 --- a/modules/yup_rhi/native/yup_GpuComputePipeline_webgpu.cpp +++ b/modules/yup_rhi/native/yup_GpuComputePipeline_webgpu.cpp @@ -31,17 +31,17 @@ class GpuComputePipelineWebGPU final : public GpuComputePipeline public: GpuComputePipelineWebGPU (wgpu::ComputePipeline pipeline, GpuWorkgroupSize wgs) : computePipeline (std::move (pipeline)) - , workgroupSize_ (wgs) + , workgroupSize (wgs) { } - GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize_; } + GpuWorkgroupSize getWorkgroupSize() const noexcept override { return workgroupSize; } wgpu::ComputePipeline getPipeline() const noexcept { return computePipeline; } private: wgpu::ComputePipeline computePipeline; - GpuWorkgroupSize workgroupSize_; + GpuWorkgroupSize workgroupSize; }; //============================================================================== diff --git a/modules/yup_rhi/native/yup_GpuDevice_d3d.cpp b/modules/yup_rhi/native/yup_GpuDevice_d3d.cpp index 89c90dde7..b431a9ade 100644 --- a/modules/yup_rhi/native/yup_GpuDevice_d3d.cpp +++ b/modules/yup_rhi/native/yup_GpuDevice_d3d.cpp @@ -23,7 +23,9 @@ #include "rive/renderer/d3d11/render_context_d3d_impl.hpp" #include "rive/renderer/d3d11/d3d11.hpp" #include "rive/renderer/ore/ore_context_d3d11.hpp" + #include + #include namespace yup @@ -32,30 +34,32 @@ namespace yup class GpuDeviceD3D : public GpuDevice { public: - GpuDeviceD3D (ComPtr gpu, - ComPtr gpuContext, + GpuDeviceD3D (ComPtr gpuToUse, + ComPtr gpuContextToUse, const rive::gpu::D3DContextOptions& contextOptions, Options options) - : m_options (options) - , m_renderContextOptions (contextOptions) - , m_gpu (std::move (gpu)) - , m_gpuContext (std::move (gpuContext)) - , m_renderContext (rive::gpu::RenderContextD3DImpl::MakeContext (m_gpu, m_gpuContext, m_renderContextOptions)) - , m_oreContext (rive::ore::ContextD3D11::Make (m_gpu.Get(), m_gpuContext.Get())) + : options (options) + , renderContextOptions (contextOptions) + , gpu (std::move (gpuToUse)) + , gpuContext (std::move (gpuContextToUse)) + , renderContext (rive::gpu::RenderContextD3DImpl::MakeContext (gpu, gpuContext, renderContextOptions)) + , oreContext (rive::ore::ContextD3D11::Make (gpu.Get(), gpuContext.Get())) { } GpuPlatform getPlatform() const noexcept override { return GpuPlatform::Direct3D; } - rive::ore::Context* gpuContext() const noexcept override { return m_oreContext.get(); } + rive::gpu::RenderContext* getRenderContext() const override { return renderContext.get(); } + + rive::ore::Context* getGpuContext() const noexcept override { return oreContext.get(); } bool isComputeAvailable() const noexcept override { return true; } /** Returns the native ID3D11Device for compute operations. */ - ID3D11Device* getD3DDevice() const noexcept { return m_gpu.Get(); } + ID3D11Device* getD3DDevice() const noexcept { return gpu.Get(); } /** Returns the native ID3D11DeviceContext for compute operations. */ - ID3D11DeviceContext* getD3DDeviceContext() const noexcept { return m_gpuContext.Get(); } + ID3D11DeviceContext* getD3DDeviceContext() const noexcept { return gpuContext.Get(); } //============================================================================== @@ -78,7 +82,7 @@ class GpuDeviceD3D : public GpuDevice initData.pSysMem = data; ComPtr d3dBuffer; - HRESULT hr = m_gpu->CreateBuffer (&bufDesc, &initData, d3dBuffer.ReleaseAndGetAddressOf()); + HRESULT hr = gpu->CreateBuffer (&bufDesc, &initData, d3dBuffer.ReleaseAndGetAddressOf()); if (FAILED (hr) || d3dBuffer == nullptr) return nullptr; @@ -89,7 +93,7 @@ class GpuDeviceD3D : public GpuDevice uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW; ComPtr uav; - hr = m_gpu->CreateUnorderedAccessView (d3dBuffer.Get(), &uavDesc, uav.ReleaseAndGetAddressOf()); + hr = gpu->CreateUnorderedAccessView (d3dBuffer.Get(), &uavDesc, uav.ReleaseAndGetAddressOf()); if (FAILED (hr) || uav == nullptr) return nullptr; @@ -116,12 +120,12 @@ class GpuDeviceD3D : public GpuDevice if (byteSize == fullSize) { - m_gpuContext->UpdateSubresource (impl->d3dStorageBuffer.Get(), 0, nullptr, data, static_cast (byteSize), 0); + gpuContext->UpdateSubresource (impl->d3dStorageBuffer.Get(), 0, nullptr, data, static_cast (byteSize), 0); } else { D3D11_BOX box { 0, 0, 0, static_cast (byteSize), 1, 1 }; - m_gpuContext->UpdateSubresource (impl->d3dStorageBuffer.Get(), 0, &box, data, static_cast (byteSize), 0); + gpuContext->UpdateSubresource (impl->d3dStorageBuffer.Get(), 0, &box, data, static_cast (byteSize), 0); } return true; @@ -184,7 +188,7 @@ class GpuDeviceD3D : public GpuDevice stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; ComPtr staging; - auto hr = m_gpu->CreateTexture2D (&stagingDesc, nullptr, staging.ReleaseAndGetAddressOf()); + auto hr = gpu->CreateTexture2D (&stagingDesc, nullptr, staging.ReleaseAndGetAddressOf()); if (FAILED (hr)) return nullptr; return staging; @@ -192,7 +196,7 @@ class GpuDeviceD3D : public GpuDevice std::unique_ptr createOffscreenTarget (int width, int height) override { - if (width <= 0 || height <= 0 || m_renderContext == nullptr) + if (width <= 0 || height <= 0 || renderContext == nullptr) return nullptr; auto target = std::make_unique(); @@ -201,8 +205,8 @@ class GpuDeviceD3D : public GpuDevice target->renderContext = nullptr; target->contextSlot = nullptr; - target->renderCanvas = m_renderContext->makeRenderCanvas (static_cast (width), - static_cast (height)); + target->renderCanvas = renderContext->makeRenderCanvas (static_cast (width), + static_cast (height)); if (target->renderCanvas == nullptr) return nullptr; @@ -268,7 +272,7 @@ class GpuDeviceD3D : public GpuDevice renderContext->flush (flushDesc); if (auto* renderTarget = static_cast (target.getRenderTarget())) - m_gpuContext->CopyResource (target.stagingTexture.Get(), renderTarget->targetTexture()); + gpuContext->CopyResource (target.stagingTexture.Get(), renderTarget->targetTexture()); target.contextSlot->frameActive = false; } @@ -287,11 +291,11 @@ class GpuDeviceD3D : public GpuDevice if (target.getRenderContext() == nullptr) { if (auto* renderTarget = static_cast (target.getRenderTarget())) - m_gpuContext->CopyResource (target.stagingTexture.Get(), renderTarget->targetTexture()); + gpuContext->CopyResource (target.stagingTexture.Get(), renderTarget->targetTexture()); } D3D11_MAPPED_SUBRESOURCE mapped {}; - HRESULT hr = m_gpuContext->Map (target.stagingTexture.Get(), 0, D3D11_MAP_READ, 0, &mapped); + HRESULT hr = gpuContext->Map (target.stagingTexture.Get(), 0, D3D11_MAP_READ, 0, &mapped); if (FAILED (hr)) return false; @@ -305,40 +309,58 @@ class GpuDeviceD3D : public GpuDevice bytesPerRow); } - m_gpuContext->Unmap (target.stagingTexture.Get(), 0); + gpuContext->Unmap (target.stagingTexture.Get(), 0); return true; } private: OffscreenContextSlot* acquireOffscreenContext() { - for (const auto& slot : m_offscreenContextPool) + for (const auto& slot : offscreenContextPool) { if (! slot->frameActive) return slot.get(); } auto slot = std::make_unique(); - slot->renderContext = rive::gpu::RenderContextD3DImpl::MakeContext (m_gpu, m_gpuContext, m_renderContextOptions); + slot->renderContext = rive::gpu::RenderContextD3DImpl::MakeContext (gpu, gpuContext, renderContextOptions); if (slot->renderContext == nullptr) return nullptr; auto* result = slot.get(); - m_offscreenContextPool.push_back (std::move (slot)); + offscreenContextPool.push_back (std::move (slot)); return result; } - Options m_options; - rive::gpu::D3DContextOptions m_renderContextOptions; - ComPtr m_gpu; - ComPtr m_gpuContext; - std::unique_ptr m_renderContext; - std::vector> m_offscreenContextPool; - std::unique_ptr m_oreContext; + Options options; + rive::gpu::D3DContextOptions renderContextOptions; + ComPtr gpu; + ComPtr gpuContext; + std::unique_ptr renderContext; + std::vector> offscreenContextPool; + std::unique_ptr oreContext; }; //============================================================================== +ID3D11Device* yup_getDirect3DDevice (GpuDevice& gpuDevice) +{ + if (gpuDevice.getPlatform() != GpuPlatform::Direct3D) + return nullptr; + + return static_cast (gpuDevice).getD3DDevice(); +} + +ID3D11DeviceContext* yup_getDirect3DDeviceContext (GpuDevice& gpuDevice) +{ + if (gpuDevice.getPlatform() != GpuPlatform::Direct3D) + return nullptr; + + return static_cast (gpuDevice).getD3DDeviceContext(); +} + +//============================================================================== + std::unique_ptr yup_constructDirect3DGpuDevice (GpuDevice::Options fiddleOptions) { ComPtr adapter; diff --git a/modules/yup_rhi/native/yup_GpuDevice_dawn.cpp b/modules/yup_rhi/native/yup_GpuDevice_dawn.cpp index ae8c561a2..78a6bfcd7 100644 --- a/modules/yup_rhi/native/yup_GpuDevice_dawn.cpp +++ b/modules/yup_rhi/native/yup_GpuDevice_dawn.cpp @@ -66,17 +66,17 @@ class GpuDeviceDawn : public GpuDevice { public: GpuDeviceDawn (Options options) - : m_options (options) + : options (options) { WGPUInstanceDescriptor instanceDescriptor {}; instanceDescriptor.features.timedWaitAnyEnable = true; - m_instance = std::make_unique (&instanceDescriptor); + instance = std::make_unique (&instanceDescriptor); wgpu::RequestAdapterOptions adapterOptions = { .powerPreference = wgpu::PowerPreference::HighPerformance, }; - auto adapters = m_instance->EnumerateAdapters (&adapterOptions); + auto adapters = instance->EnumerateAdapters (&adapterOptions); wgpu::DawnAdapterPropertiesPowerPreference power_props {}; wgpu::AdapterProperties adapterProperties {}; @@ -118,20 +118,22 @@ class GpuDeviceDawn : public GpuDevice .requiredFeatures = requiredFeatures.data(), }; - m_backendDevice = preferredAdapter->CreateDevice (&deviceDesc); + backendDevice = preferredAdapter->CreateDevice (&deviceDesc); DawnProcTable backendProcs = dawn::native::GetProcs(); dawnProcSetProcs (&backendProcs); - backendProcs.deviceSetUncapturedErrorCallback (m_backendDevice, print_device_error, nullptr); - backendProcs.deviceSetDeviceLostCallback (m_backendDevice, device_lost_callback, nullptr); + backendProcs.deviceSetUncapturedErrorCallback (backendDevice, print_device_error, nullptr); + backendProcs.deviceSetDeviceLostCallback (backendDevice, device_lost_callback, nullptr); - m_device = wgpu::Device::Acquire (m_backendDevice); - m_queue = m_device.GetQueue(); + device = wgpu::Device::Acquire (backendDevice); + queue = device.GetQueue(); } GpuPlatform getPlatform() const noexcept override { return GpuPlatform::WebGPU; } - rive::ore::Context* gpuContext() const noexcept override { return nullptr; } + rive::gpu::RenderContext* getRenderContext() const override { return nullptr; } + + rive::ore::Context* getGpuContext() const noexcept override { return nullptr; } bool isComputeAvailable() const noexcept override { return true; } @@ -148,11 +150,11 @@ class GpuDeviceDawn : public GpuDevice bufDesc.size = byteSize; bufDesc.label = "GpuBuffer storage"; - wgpu::Buffer wgpuBuffer = m_device.CreateBuffer (&bufDesc); + wgpu::Buffer wgpuBuffer = device.CreateBuffer (&bufDesc); if (wgpuBuffer == nullptr) return nullptr; - m_queue.WriteBuffer (wgpuBuffer, 0, data, byteSize); + queue.WriteBuffer (wgpuBuffer, 0, data, byteSize); return GpuBuffer::createWithImpl (GpuBuffer::Impl { type, byteSize, {}, std::move (wgpuBuffer) }); } @@ -172,7 +174,7 @@ class GpuDeviceDawn : public GpuDevice if (byteSize > buffer->getSizeInBytes()) return false; - m_queue.WriteBuffer (impl->webgpuStorageBuffer, 0, data, byteSize); + queue.WriteBuffer (impl->webgpuStorageBuffer, 0, data, byteSize); return true; } @@ -188,20 +190,20 @@ class GpuDeviceDawn : public GpuDevice bool readOffscreenPixels (OffscreenTarget&, void*, size_t) override { return false; } /** Returns the native WGPU device for compute operations. */ - WGPUDevice getBackendDevice() const noexcept { return m_backendDevice; } + WGPUDevice getBackendDevice() const noexcept { return backendDevice; } /** Returns the wgpu::Device for compute operations. */ - wgpu::Device getDevice() const noexcept { return m_device; } + wgpu::Device getDevice() const noexcept { return device; } /** Returns the wgpu::Queue for compute operations. */ - wgpu::Queue getQueue() const noexcept { return m_queue; } + wgpu::Queue getQueue() const noexcept { return queue; } private: - Options m_options; - WGPUDevice m_backendDevice = {}; - wgpu::Device m_device = {}; - wgpu::Queue m_queue = {}; - std::unique_ptr m_instance; + Options options; + WGPUDevice backendDevice = {}; + wgpu::Device device = {}; + wgpu::Queue queue = {}; + std::unique_ptr instance; }; //============================================================================== diff --git a/modules/yup_rhi/native/yup_GpuDevice_metal.cpp b/modules/yup_rhi/native/yup_GpuDevice_metal.cpp index 59dbc2026..bc80e79eb 100644 --- a/modules/yup_rhi/native/yup_GpuDevice_metal.cpp +++ b/modules/yup_rhi/native/yup_GpuDevice_metal.cpp @@ -35,25 +35,27 @@ class GpuDeviceMetal : public GpuDevice { public: GpuDeviceMetal (GpuDevice::Options options) - : fiddleOptions (options) + : options (options) { - rive::gpu::RenderContextMetalImpl::ContextOptions renderCtxOpts; + rive::gpu::RenderContextMetalImpl::ContextOptions renderContexOptions; - if (fiddleOptions.synchronousShaderCompilations) - renderCtxOpts.shaderCompilationMode = rive::gpu::ShaderCompilationMode::alwaysSynchronous; + if (options.synchronousShaderCompilations) + renderContexOptions.shaderCompilationMode = rive::gpu::ShaderCompilationMode::alwaysSynchronous; - if (fiddleOptions.disableRasterOrdering) - renderCtxOpts.disableFramebufferReads = true; + if (options.disableRasterOrdering) + renderContexOptions.disableFramebufferReads = true; - renderContext = rive::gpu::RenderContextMetalImpl::MakeContext (gpu, renderCtxOpts); - oreContext = rive::ore::ContextMetal::Make (gpu, queue); + renderContext = rive::gpu::RenderContextMetalImpl::MakeContext (device, renderContexOptions); + oreContext = rive::ore::ContextMetal::Make (device, queue); } //============================================================================== GpuPlatform getPlatform() const noexcept override { return GpuPlatform::Metal; } - rive::ore::Context* gpuContext() const noexcept override { return oreContext.get(); } + rive::gpu::RenderContext* getRenderContext() const override { return renderContext.get(); } + + rive::ore::Context* getGpuContext() const noexcept override { return oreContext.get(); } bool isComputeAvailable() const noexcept override { return true; } @@ -68,9 +70,9 @@ class GpuDeviceMetal : public GpuDevice return nullptr; MTLResourceOptions options = MTLResourceStorageModeShared; - id mtlBuffer = [gpu newBufferWithBytes:data - length:byteSize - options:options]; + id mtlBuffer = [device newBufferWithBytes:data + length:byteSize + options:options]; if (mtlBuffer == nil) return nullptr; @@ -101,8 +103,8 @@ class GpuDeviceMetal : public GpuDevice // dispatch has finished, then read directly from the shared buffer. // Avoids a full-size staging allocation + blit (~2 MB for the // particle demo) by exploiting Apple Silicon's unified memory. - id fenceBuf = [gpu newBufferWithLength:4 - options:MTLResourceStorageModeShared]; + id fenceBuf = [device newBufferWithLength:4 + options:MTLResourceStorageModeShared]; if (fenceBuf == nil) return false; @@ -139,8 +141,8 @@ class GpuDeviceMetal : public GpuDevice YUP_AUTORELEASEPOOL { // Serialise after all prior GPU work with a blit, then write. - id stagingBuf = [gpu newBufferWithLength:4 - options:MTLResourceStorageModeShared]; + id stagingBuf = [device newBufferWithLength:4 + options:MTLResourceStorageModeShared]; id cmd = [queue commandBuffer]; id blit = [cmd blitCommandEncoder]; @@ -250,7 +252,7 @@ class GpuDeviceMetal : public GpuDevice #else stagingDesc.storageMode = MTLStorageModeManaged; #endif - target.stagingTexture = [gpu newTextureWithDescriptor:stagingDesc]; + target.stagingTexture = [device newTextureWithDescriptor:stagingDesc]; if (target.stagingTexture == nil) return false; } @@ -292,7 +294,7 @@ class GpuDeviceMetal : public GpuDevice //============================================================================== /** Returns the native MTLDevice. Used by GraphicsContextMetal to share the device. */ - id getDevice() const noexcept { return gpu; } + id getDevice() const noexcept { return device; } /** Returns the native MTLCommandQueue. Used by GraphicsContextMetal. */ id getCommandQueue() const noexcept { return queue; } @@ -343,8 +345,10 @@ class GpuDeviceMetal : public GpuDevice { if (renderCanvas == nullptr) return nil; + if (auto* target = static_cast (renderCanvas->renderTarget())) return target->targetTexture(); + return nil; } }; @@ -358,13 +362,13 @@ class GpuDeviceMetal : public GpuDevice } rive::gpu::RenderContextMetalImpl::ContextOptions renderCtxOpts; - if (fiddleOptions.synchronousShaderCompilations) + if (options.synchronousShaderCompilations) renderCtxOpts.shaderCompilationMode = rive::gpu::ShaderCompilationMode::alwaysSynchronous; - if (fiddleOptions.disableRasterOrdering) + if (options.disableRasterOrdering) renderCtxOpts.disableFramebufferReads = true; auto slot = std::make_unique(); - slot->renderContext = rive::gpu::RenderContextMetalImpl::MakeContext (gpu, renderCtxOpts); + slot->renderContext = rive::gpu::RenderContextMetalImpl::MakeContext (device, renderCtxOpts); if (slot->renderContext == nullptr) return nullptr; @@ -373,12 +377,12 @@ class GpuDeviceMetal : public GpuDevice return result; } - const GpuDevice::Options fiddleOptions; + const GpuDevice::Options options; std::unique_ptr renderContext; std::vector> offscreenContextPool; std::unique_ptr oreContext; - id gpu = MTLCreateSystemDefaultDevice(); - id queue = [gpu newCommandQueue]; + id device = MTLCreateSystemDefaultDevice(); + id queue = [device newCommandQueue]; }; //============================================================================== diff --git a/modules/yup_rhi/native/yup_GpuDevice_opengl.cpp b/modules/yup_rhi/native/yup_GpuDevice_opengl.cpp index 8248eb9a5..f8752eeca 100644 --- a/modules/yup_rhi/native/yup_GpuDevice_opengl.cpp +++ b/modules/yup_rhi/native/yup_GpuDevice_opengl.cpp @@ -24,17 +24,49 @@ #include "rive/renderer/gl/render_context_gl_impl.hpp" #include "rive/renderer/gl/render_target_gl.hpp" #include "rive/renderer/ore/ore_context_gl.hpp" + #include #include namespace yup { +#if RIVE_DESKTOP_GL && DEBUG +static void GLAPIENTRY err_msg_callback (GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) +{ + if (type == GL_DEBUG_TYPE_ERROR_KHR) + { + printf ("GL ERROR: %s\n", message); + fflush (stdout); + assert (false); + } + else if (type == GL_DEBUG_TYPE_PERFORMANCE_KHR) + { + if (strcmp (message, "API_ID_REDUNDANT_FBO performance warning has been generated. Redundant state " + "change in glBindFramebuffer API call, FBO 0, \"\", already bound.") + == 0) + return; + if (strstr (message, "is being recompiled based on GL state.")) + return; + printf ("GL PERF: %s\n", message); + fflush (stdout); + } +} +#endif + +//============================================================================== class GpuDeviceGL : public GpuDevice { public: + //============================================================================== GpuDeviceGL (Options options) - : m_options (options) + : options (options) { #if RIVE_DESKTOP_GL if (! gladLoadCustomLoader ((GLADloadfunc) options.loaderFunction)) @@ -44,26 +76,46 @@ class GpuDeviceGL : public GpuDevice } #endif - m_renderContext = rive::gpu::RenderContextGLImpl::MakeContext (m_renderContextOptions); - if (! m_renderContext) + renderContext = rive::gpu::RenderContextGLImpl::MakeContext (renderContextOptions); + if (! renderContext) { fprintf (stderr, "Failed to create a renderer.\n"); return; } - m_oreContext = rive::ore::ContextGL::Make(); + oreContext = rive::ore::ContextGL::Make(); + +#if YUP_ENABLE_GL_VERBOSE + printf ("GL_VENDOR: %s\n", glGetString (GL_VENDOR)); + printf ("GL_RENDERER: %s\n", glGetString (GL_RENDERER)); + printf ("GL_VERSION: %s\n", glGetString (GL_VERSION)); + +#if RIVE_DESKTOP_GL + printf ("GL_ANGLE_shader_pixel_local_storage_coherent: %i\n", GLAD_GL_ANGLE_shader_pixel_local_storage_coherent); +#endif + +#if ! RIVE_ANDROID + int n; + glGetIntegerv (GL_NUM_EXTENSIONS, &n); + for (size_t i = 0; i < n; ++i) + printf (" %s\n", glGetStringi (GL_EXTENSIONS, i)); +#endif +#endif // YUP_ENABLE_GL_VERBOSE #if RIVE_DESKTOP_GL && DEBUG if (GLAD_GL_KHR_debug) { glEnable (GL_DEBUG_OUTPUT_KHR); glDebugMessageControlKHR (GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); + glDebugMessageCallbackKHR (&err_msg_callback, nullptr); } #endif } ~GpuDeviceGL() override = default; + //============================================================================== + GpuPlatform getPlatform() const noexcept override { #if RIVE_ANDROID || RIVE_WEBGL @@ -73,7 +125,11 @@ class GpuDeviceGL : public GpuDevice #endif } - rive::ore::Context* gpuContext() const noexcept override { return m_oreContext.get(); } + rive::gpu::RenderContext* getRenderContext() const override { return renderContext.get(); } + + rive::ore::Context* getGpuContext() const noexcept override { return oreContext.get(); } + + //============================================================================== bool isComputeAvailable() const noexcept override { @@ -164,16 +220,23 @@ class GpuDeviceGL : public GpuDevice return false; auto* impl = buffer->getImpl(); - if (impl == nullptr || impl->glBuffer == 0) + if (impl == nullptr) return false; - if (byteSize > buffer->getSizeInBytes()) - return false; + // For native GL storage buffers, use glBufferSubData. + if (impl->glBuffer != 0) + { + if (byteSize > buffer->getSizeInBytes()) + return false; - glBindBuffer (GL_SHADER_STORAGE_BUFFER, impl->glBuffer); - glBufferSubData (GL_SHADER_STORAGE_BUFFER, 0, static_cast (byteSize), data); - glBindBuffer (GL_SHADER_STORAGE_BUFFER, 0); - return true; + glBindBuffer (GL_SHADER_STORAGE_BUFFER, impl->glBuffer); + glBufferSubData (GL_SHADER_STORAGE_BUFFER, 0, static_cast (byteSize), data); + glBindBuffer (GL_SHADER_STORAGE_BUFFER, 0); + return true; + } + + // For ore-backed buffers (vertex, index, uniform), delegate to base class. + return GpuDevice::updateBuffer (buffer, data, byteSize); } //============================================================================== @@ -251,10 +314,10 @@ class GpuDeviceGL : public GpuDevice std::unique_ptr createOffscreenTarget (int width, int height) override { - if (width <= 0 || height <= 0 || m_renderContext == nullptr) + if (width <= 0 || height <= 0 || renderContext == nullptr) return nullptr; - auto renderCanvas = m_renderContext->makeRenderCanvas (static_cast (width), static_cast (height)); + auto renderCanvas = renderContext->makeRenderCanvas (static_cast (width), static_cast (height)); if (renderCanvas == nullptr) return nullptr; @@ -262,7 +325,7 @@ class GpuDeviceGL : public GpuDevice target->width = width; target->height = height; target->renderContext = nullptr; - target->mirrorContext = m_renderContext.get(); + target->mirrorContext = renderContext.get(); target->contextSlot = nullptr; target->renderCanvas = std::move (renderCanvas); return target; @@ -331,16 +394,17 @@ class GpuDeviceGL : public GpuDevice glReadPixels (0, 0, target.width, target.height, GL_RGBA, GL_UNSIGNED_BYTE, dst); glBindFramebuffer (GL_READ_FRAMEBUFFER, 0); + // Flip vertically: OpenGL framebuffer origin is bottom-left. + offscreenPixelsRow.resize (bytesPerRow); auto* bytes = static_cast (dst); - std::vector rowBuffer (bytesPerRow); const int halfHeight = target.height / 2; for (int i = 0; i < halfHeight; ++i) { uint8_t* top = bytes + static_cast (i) * bytesPerRow; uint8_t* bottom = bytes + static_cast (target.height - 1 - i) * bytesPerRow; - std::memcpy (rowBuffer.data(), top, bytesPerRow); + std::memcpy (offscreenPixelsRow.data(), top, bytesPerRow); std::memcpy (top, bottom, bytesPerRow); - std::memcpy (bottom, rowBuffer.data(), bytesPerRow); + std::memcpy (bottom, offscreenPixelsRow.data(), bytesPerRow); } return true; @@ -349,27 +413,28 @@ class GpuDeviceGL : public GpuDevice private: OffscreenContextSlot* acquireOffscreenContext() { - for (const auto& slot : m_offscreenContextPool) + for (const auto& slot : offscreenContextPool) { if (! slot->frameActive) return slot.get(); } auto slot = std::make_unique(); - slot->renderContext = rive::gpu::RenderContextGLImpl::MakeContext (m_renderContextOptions); + slot->renderContext = rive::gpu::RenderContextGLImpl::MakeContext (renderContextOptions); if (slot->renderContext == nullptr) return nullptr; auto* result = slot.get(); - m_offscreenContextPool.push_back (std::move (slot)); + offscreenContextPool.push_back (std::move (slot)); return result; } - Options m_options; - rive::gpu::RenderContextGLImpl::ContextOptions m_renderContextOptions; - std::unique_ptr m_renderContext; - std::vector> m_offscreenContextPool; - std::unique_ptr m_oreContext; + Options options; + rive::gpu::RenderContextGLImpl::ContextOptions renderContextOptions; + std::unique_ptr renderContext; + std::vector> offscreenContextPool; + std::unique_ptr oreContext; + std::vector offscreenPixelsRow; }; //============================================================================== diff --git a/modules/yup_rhi/native/yup_GpuDevice_webgpu.cpp b/modules/yup_rhi/native/yup_GpuDevice_webgpu.cpp index 231b90580..a5b8c1554 100644 --- a/modules/yup_rhi/native/yup_GpuDevice_webgpu.cpp +++ b/modules/yup_rhi/native/yup_GpuDevice_webgpu.cpp @@ -38,40 +38,42 @@ class GpuDeviceWebGPU : public GpuDevice { public: GpuDeviceWebGPU (Options options) - : m_options (options) + : options (options) { - m_device = wgpu::Device::Acquire (emscripten_webgpu_get_device()); - if (m_device == nullptr) + device = wgpu::Device::Acquire (emscripten_webgpu_get_device()); + if (device == nullptr) { fprintf (stderr, "WebGPU: no device. Ensure Module.preinitializedWebGPUDevice is set before main().\n"); return; } - m_queue = m_device.GetQueue(); + queue = device.GetQueue(); - m_renderContext = rive::gpu::RenderContextWebGPUImpl::MakeContext ( - {}, m_device, m_queue, rive::gpu::RenderContextWebGPUImpl::ContextOptions()); + renderContext = rive::gpu::RenderContextWebGPUImpl::MakeContext ( + {}, device, queue, rive::gpu::RenderContextWebGPUImpl::ContextOptions()); - if (m_renderContext == nullptr) + if (renderContext == nullptr) { fprintf (stderr, "WebGPU: failed to create a render context.\n"); return; } - m_oreContext = m_renderContext->static_impl_cast()->makeOreContext(); + oreContext = renderContext->static_impl_cast()->makeOreContext(); } GpuPlatform getPlatform() const noexcept override { return GpuPlatform::WebGPU; } - rive::ore::Context* gpuContext() const noexcept override { return m_oreContext.get(); } + rive::gpu::RenderContext* getRenderContext() const override { return renderContext.get(); } + + rive::ore::Context* getGpuContext() const noexcept override { return oreContext.get(); } bool isComputeAvailable() const noexcept override { return true; } /** Returns the native wgpu::Device for compute operations. */ - wgpu::Device getWgpuDevice() const noexcept { return m_device; } + wgpu::Device getWgpuDevice() const noexcept { return device; } /** Returns the native wgpu::Queue for compute operations. */ - wgpu::Queue getWgpuQueue() const noexcept { return m_queue; } + wgpu::Queue getWgpuQueue() const noexcept { return queue; } //============================================================================== @@ -88,11 +90,11 @@ class GpuDeviceWebGPU : public GpuDevice bufDesc.size = byteSize; bufDesc.label = "GpuBuffer storage"; - wgpu::Buffer wgpuBuffer = m_device.CreateBuffer (&bufDesc); + wgpu::Buffer wgpuBuffer = device.CreateBuffer (&bufDesc); if (wgpuBuffer == nullptr) return nullptr; - m_queue.WriteBuffer (wgpuBuffer, 0, data, byteSize); + queue.WriteBuffer (wgpuBuffer, 0, data, byteSize); return GpuBuffer::createWithImpl (GpuBuffer::Impl { type, byteSize, {}, std::move (wgpuBuffer) }); } @@ -114,7 +116,7 @@ class GpuDeviceWebGPU : public GpuDevice if (byteSize > buffer->getSizeInBytes()) return false; - m_queue.WriteBuffer (impl->webgpuStorageBuffer, 0, data, byteSize); + queue.WriteBuffer (impl->webgpuStorageBuffer, 0, data, byteSize); return true; } @@ -163,7 +165,7 @@ class GpuDeviceWebGPU : public GpuDevice std::unique_ptr createOffscreenTarget (int width, int height) override { - if (width <= 0 || height <= 0 || m_renderContext == nullptr) + if (width <= 0 || height <= 0 || renderContext == nullptr) return nullptr; auto target = std::make_unique(); @@ -171,8 +173,8 @@ class GpuDeviceWebGPU : public GpuDevice target->height = height; target->renderContext = nullptr; target->contextSlot = nullptr; - target->renderCanvas = m_renderContext->makeRenderCanvas (static_cast (width), - static_cast (height)); + target->renderCanvas = renderContext->makeRenderCanvas (static_cast (width), + static_cast (height)); if (target->renderCanvas == nullptr) return nullptr; @@ -221,13 +223,13 @@ class GpuDeviceWebGPU : public GpuDevice if (renderContext == nullptr || target.contextSlot == nullptr || ! target.contextSlot->frameActive) return; - wgpu::CommandEncoder encoder = m_device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); renderContext->flush ({ .renderTarget = target.getRenderTarget(), .externalCommandBuffer = encoder.Get() }); wgpu::CommandBuffer commands = encoder.Finish(); - m_queue.Submit (1, &commands); + queue.Submit (1, &commands); target.contextSlot->frameActive = false; } @@ -240,7 +242,7 @@ class GpuDeviceWebGPU : public GpuDevice private: OffscreenContextSlot* acquireOffscreenContext() { - for (const auto& slot : m_offscreenContextPool) + for (const auto& slot : offscreenContextPool) { if (! slot->frameActive) return slot.get(); @@ -248,21 +250,21 @@ class GpuDeviceWebGPU : public GpuDevice auto slot = std::make_unique(); slot->renderContext = rive::gpu::RenderContextWebGPUImpl::MakeContext ( - {}, m_device, m_queue, rive::gpu::RenderContextWebGPUImpl::ContextOptions()); + {}, device, queue, rive::gpu::RenderContextWebGPUImpl::ContextOptions()); if (slot->renderContext == nullptr) return nullptr; auto* result = slot.get(); - m_offscreenContextPool.push_back (std::move (slot)); + offscreenContextPool.push_back (std::move (slot)); return result; } - Options m_options; - wgpu::Device m_device; - wgpu::Queue m_queue; - std::unique_ptr m_renderContext; - std::vector> m_offscreenContextPool; - std::unique_ptr m_oreContext; + Options options; + wgpu::Device device; + wgpu::Queue queue; + std::unique_ptr renderContext; + std::vector> offscreenContextPool; + std::unique_ptr oreContext; }; //============================================================================== diff --git a/modules/yup_rhi/rhi/yup_GpuFrame.cpp b/modules/yup_rhi/rhi/yup_GpuFrame.cpp index 377baae84..a00b9fa59 100644 --- a/modules/yup_rhi/rhi/yup_GpuFrame.cpp +++ b/modules/yup_rhi/rhi/yup_GpuFrame.cpp @@ -54,7 +54,7 @@ GpuFrame GpuFrame::begin (GpuDevice::Ptr ctx) { GpuFrame frame; - auto* oreCtx = ctx->gpuContext(); + auto* oreCtx = ctx->getGpuContext(); if (oreCtx == nullptr) return frame; diff --git a/modules/yup_rhi/rhi/yup_GpuPipeline.cpp b/modules/yup_rhi/rhi/yup_GpuPipeline.cpp index 6a111b86c..98fc92527 100644 --- a/modules/yup_rhi/rhi/yup_GpuPipeline.cpp +++ b/modules/yup_rhi/rhi/yup_GpuPipeline.cpp @@ -268,7 +268,7 @@ ResultValue GpuPipeline::compile (GpuDevice::Ptr ctx, { using namespace GpuPipelineHelpers; - auto oreCtx = ctx->gpuContext(); + auto oreCtx = ctx->getGpuContext(); if (oreCtx == nullptr) return makeResultValueFail ("GpuDevice was not created with Options::enableOreContext = true"); diff --git a/modules/yup_rhi/rhi/yup_GpuRenderPass.cpp b/modules/yup_rhi/rhi/yup_GpuRenderPass.cpp index dafee8cf2..3b5998596 100644 --- a/modules/yup_rhi/rhi/yup_GpuRenderPass.cpp +++ b/modules/yup_rhi/rhi/yup_GpuRenderPass.cpp @@ -461,6 +461,28 @@ bool GpuRenderPass::finish() if (i == nullptr || i->finished) return false; + // When a clear was requested but no draw was submitted (no pipeline set), + // encode a clear-only render pass so the framebuffer is actually cleared. + if (i->options.clear && i->orePipeline == nullptr && i->oreCtx != nullptr && i->outputTexture != nullptr) + { + auto outputView = Impl::createView (*i->oreCtx, *i->outputTexture, true); + if (outputView != nullptr) + { + rive::ore::RenderPassDesc rpDesc; + rpDesc.colorCount = 1; + rpDesc.colorAttachments[0].view = outputView.get(); + rpDesc.colorAttachments[0].loadOp = rive::ore::LoadOp::clear; + rpDesc.colorAttachments[0].storeOp = rive::ore::StoreOp::store; + rpDesc.colorAttachments[0].clearColor = { i->options.clearColor.red, + i->options.clearColor.green, + i->options.clearColor.blue, + i->options.clearColor.alpha }; + + auto renderPass = i->oreCtx->beginRenderPass (rpDesc); + renderPass->finish(); + } + } + i->finished = true; return true; } diff --git a/modules/yup_rhi/yup_rhi.h b/modules/yup_rhi/yup_rhi.h index 7b0f5bc7a..ce37e3696 100644 --- a/modules/yup_rhi/yup_rhi.h +++ b/modules/yup_rhi/yup_rhi.h @@ -32,7 +32,7 @@ website: https://github.com/kunitoki/yup license: ISC - dependencies: yup_core yup_shading rive_renderer + dependencies: yup_core yup_shading yup_simd rive_renderer appleFrameworks: Metal END_YUP_MODULE_DECLARATION @@ -45,6 +45,7 @@ #include #include +#include //============================================================================== YUP_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") diff --git a/modules/yup_simd/buffers/yup_AffineTransformOperations.cpp b/modules/yup_simd/buffers/yup_AffineTransformOperations.cpp index 6d88aa991..683b4897c 100644 --- a/modules/yup_simd/buffers/yup_AffineTransformOperations.cpp +++ b/modules/yup_simd/buffers/yup_AffineTransformOperations.cpp @@ -31,17 +31,17 @@ void YUP_CALLTYPE AffineTransformOperations::transformPoints (const float* srcXs { int i = 0; - const auto sx4 = Float4::broadcast (sx); - const auto shx4 = Float4::broadcast (shx); - const auto tx4 = Float4::broadcast (tx); - const auto shy4 = Float4::broadcast (shy); - const auto sy4 = Float4::broadcast (sy); - const auto ty4 = Float4::broadcast (ty); - - for (; i + Float4::size <= numPoints; i += Float4::size) + const auto sx4 = Float32x4::broadcast (sx); + const auto shx4 = Float32x4::broadcast (shx); + const auto tx4 = Float32x4::broadcast (tx); + const auto shy4 = Float32x4::broadcast (shy); + const auto sy4 = Float32x4::broadcast (sy); + const auto ty4 = Float32x4::broadcast (ty); + + for (; i + Float32x4::size <= numPoints; i += Float32x4::size) { - const auto x = Float4::loadUnaligned (srcXs + i); - const auto y = Float4::loadUnaligned (srcYs + i); + const auto x = Float32x4::loadUnaligned (srcXs + i); + const auto y = Float32x4::loadUnaligned (srcYs + i); const auto outX = tx4.mulAdd (sx4, x).mulAdd (shx4, y); const auto outY = ty4.mulAdd (shy4, x).mulAdd (sy4, y); diff --git a/modules/yup_simd/buffers/yup_ColorVectorOperations.cpp b/modules/yup_simd/buffers/yup_ColorVectorOperations.cpp index dbde342f1..ac3d133ff 100644 --- a/modules/yup_simd/buffers/yup_ColorVectorOperations.cpp +++ b/modules/yup_simd/buffers/yup_ColorVectorOperations.cpp @@ -24,6 +24,7 @@ namespace yup namespace { + static uint32 premultiplyComponent (uint32 component, uint32 alpha) noexcept { return (component * alpha + 127u) / 255u; @@ -38,7 +39,29 @@ static uint32 packRGBA (uint32 red, uint32 green, uint32 blue, uint32 alpha) noe //============================================================================== void YUP_CALLTYPE ColorVectorOperations::premultiplyARGB (uint32* pixels, int numPixels) noexcept { - for (int i = 0; i < numPixels; ++i) + const auto c127 = Uint32x4 (127u); + const auto c255 = Uint32x4 (255u); + const auto mask8 = Uint32x4 (0xFFu); + + int i = 0; + + for (; i + 4 <= numPixels; i += 4) + { + auto p = Uint32x4::loadUnaligned (pixels + i); + const auto a = p >> 24; + + auto r = ((p >> 16) & mask8) * a; + auto g = ((p >> 8) & mask8) * a; + auto b = (p & mask8) * a; + + r = (r + c127) / c255; + g = (g + c127) / c255; + b = (b + c127) / c255; + + ((a << 24) | (r << 16) | (g << 8) | b).storeUnaligned (pixels + i); + } + + for (; i < numPixels; ++i) { const uint32 pixel = pixels[i]; const uint32 alpha = (pixel >> 24) & 0xffu; @@ -50,62 +73,118 @@ void YUP_CALLTYPE ColorVectorOperations::premultiplyARGB (uint32* pixels, int nu } } -void YUP_CALLTYPE ColorVectorOperations::premultiplyRGBA (uint8* pixels, int numPixels) noexcept +void YUP_CALLTYPE ColorVectorOperations::premultiplyRGBA (uint32* pixels, int numPixels) noexcept { - auto* pixel = pixels; + const auto c127 = Uint32x4 (127u); + const auto c255 = Uint32x4 (255u); + const auto mask8 = Uint32x4 (0xFFu); - for (int i = 0; i < numPixels; ++i) + int i = 0; + + for (; i + 4 <= numPixels; i += 4) { - const uint32 alpha = pixel[3]; + auto p = Uint32x4::loadUnaligned (pixels + i); + const auto a = p >> 24; + + auto r = (p & mask8) * a; + auto g = ((p >> 8) & mask8) * a; + auto b = ((p >> 16) & mask8) * a; + + r = (r + c127) / c255; + g = (g + c127) / c255; + b = (b + c127) / c255; + + ((a << 24) | (b << 16) | (g << 8) | r).storeUnaligned (pixels + i); + } + + for (; i < numPixels; ++i) + { + const uint32 pixel = pixels[i]; + const uint32 alpha = pixel >> 24; + const uint32 r = premultiplyComponent (pixel & 0xFFu, alpha); + const uint32 g = premultiplyComponent ((pixel >> 8) & 0xFFu, alpha); + const uint32 b = premultiplyComponent ((pixel >> 16) & 0xFFu, alpha); - pixel[0] = static_cast (premultiplyComponent (pixel[0], alpha)); - pixel[1] = static_cast (premultiplyComponent (pixel[1], alpha)); - pixel[2] = static_cast (premultiplyComponent (pixel[2], alpha)); - pixel += 4; + pixels[i] = (alpha << 24) | (b << 16) | (g << 8) | r; } } void YUP_CALLTYPE ColorVectorOperations::convertARGBtoRGBA (const uint32* src, uint32* dst, int numPixels) noexcept { - for (int i = 0; i < numPixels; ++i) + int i = 0; + + for (; i + 4 <= numPixels; i += 4) + { + const auto p = Uint32x4::loadUnaligned (src + i); + ((p << 8) | (p >> 24)).storeUnaligned (dst + i); + } + + for (; i < numPixels; ++i) { const uint32 pixel = src[i]; dst[i] = ((pixel & 0x00ffffffu) << 8) | ((pixel >> 24) & 0xffu); } } -void YUP_CALLTYPE ColorVectorOperations::convertGrayscaleToRGBA (const uint8* src, uint8* dst, int numPixels) noexcept +//============================================================================== +void YUP_CALLTYPE ColorVectorOperations::convertBGRAtoRGBA (uint32* pixels, int numPixels) noexcept +{ + if (numPixels <= 0) + return; + + const auto maskAG = Uint32x4 (0xFF00FF00u); + const auto maskFF = Uint32x4 (0xFFu); + + const int simdPixels = numPixels & ~3; + int i = 0; + + for (; i < simdPixels; i += 4) + { + const auto p = Uint32x4::loadUnaligned (pixels + i); + + // BGRA (uint32 little-endian): bytes [B,G,R,A] + // RGBA: bytes [R,G,B,A] + // Transformation: swap byte 0 (B) ↔ byte 2 (R), keep bytes 1 (G) and 3 (A). + const auto result = (p & maskAG) | ((p & maskFF) << 16) | ((p >> 16) & maskFF); + result.storeUnaligned (pixels + i); + } + + // Scalar tail for remaining < 4 pixels. + for (; i < numPixels; ++i) + { + const auto p = pixels[i]; + pixels[i] = (p & 0xFF00FF00u) | ((p & 0xFFu) << 16) | ((p >> 16) & 0xFFu); + } +} + +void YUP_CALLTYPE ColorVectorOperations::convertGrayscaleToRGBA (const uint8* src, uint32* dst, int numPixels) noexcept { for (int i = 0; i < numPixels; ++i) { const uint32 value = *src++; - const auto rgba = packRGBA (value, value, value, 255u); - std::memcpy (dst, &rgba, sizeof (rgba)); - dst += 4; + dst[i] = packRGBA (value, value, value, 255u); } } -void YUP_CALLTYPE ColorVectorOperations::convertRGBToRGBA (const uint8* src, uint8* dst, int numPixels) noexcept +void YUP_CALLTYPE ColorVectorOperations::convertRGBToRGBA (const uint8* src, uint32* dst, int numPixels) noexcept { for (int i = 0; i < numPixels; ++i) { - const auto rgba = packRGBA (src[0], src[1], src[2], 255u); - std::memcpy (dst, &rgba, sizeof (rgba)); + dst[i] = packRGBA (src[0], src[1], src[2], 255u); src += 3; - dst += 4; } } void YUP_CALLTYPE ColorVectorOperations::lerpRows (const float* rowA, const float* rowB, float* dst, float t, int numPixels) noexcept { - const auto t4 = Float4::broadcast (t); - const auto minusOne = Float4::broadcast (-1.0f); + const auto t4 = Float32x4::broadcast (t); + const auto minusOne = Float32x4::broadcast (-1.0f); int i = 0; for (; i < numPixels; ++i) { - const auto a = Float4::loadUnaligned (rowA + i * 4); - const auto b = Float4::loadUnaligned (rowB + i * 4); + const auto a = Float32x4::loadUnaligned (rowA + i * 4); + const auto b = Float32x4::loadUnaligned (rowB + i * 4); a.mulAdd (b + (a * minusOne), t4).storeUnaligned (dst + i * 4); } } diff --git a/modules/yup_simd/buffers/yup_ColorVectorOperations.h b/modules/yup_simd/buffers/yup_ColorVectorOperations.h index aabc19982..c687a5fa4 100644 --- a/modules/yup_simd/buffers/yup_ColorVectorOperations.h +++ b/modules/yup_simd/buffers/yup_ColorVectorOperations.h @@ -31,8 +31,8 @@ class YUP_API ColorVectorOperations /** Premultiplies alpha in-place on a row of packed `0xAARRGGBB` pixels. */ static void YUP_CALLTYPE premultiplyARGB (uint32* pixels, int numPixels) noexcept; - /** Premultiplies alpha in-place on a row of RGBA byte pixels. */ - static void YUP_CALLTYPE premultiplyRGBA (uint8* pixels, int numPixels) noexcept; + /** Premultiplies alpha in-place on a row of packed RGBA pixels. */ + static void YUP_CALLTYPE premultiplyRGBA (uint32* pixels, int numPixels) noexcept; /** Converts packed `0xAARRGGBB` pixels to packed `0xRRGGBBAA` pixels. @@ -40,11 +40,20 @@ class YUP_API ColorVectorOperations */ static void YUP_CALLTYPE convertARGBtoRGBA (const uint32* src, uint32* dst, int numPixels) noexcept; - /** Expands 8-bit grayscale pixels to RGBA byte pixels with opaque alpha. */ - static void YUP_CALLTYPE convertGrayscaleToRGBA (const uint8* src, uint8* dst, int numPixels) noexcept; + /** Swaps the R and B channels of packed BGRA pixels to RGBA in place. - /** Expands RGB byte pixels to RGBA byte pixels with opaque alpha. */ - static void YUP_CALLTYPE convertRGBToRGBA (const uint8* src, uint8* dst, int numPixels) noexcept; + Processes 4 pixels per iteration using xsimd; falls back to a + scalar R ↔ B swap loop for the tail. + + The pixel count must accurately reflect the number of pixels in `pixels`. + */ + static void YUP_CALLTYPE convertBGRAtoRGBA (uint32* pixels, int numPixels) noexcept; + + /** Expands 8-bit grayscale pixels to packed RGBA pixels with opaque alpha. */ + static void YUP_CALLTYPE convertGrayscaleToRGBA (const uint8* src, uint32* dst, int numPixels) noexcept; + + /** Expands RGB byte pixels to packed RGBA pixels with opaque alpha. */ + static void YUP_CALLTYPE convertRGBToRGBA (const uint8* src, uint32* dst, int numPixels) noexcept; /** Blends rows of float RGBA pixels using `dst = rowA + (rowB - rowA) * t`. */ static void YUP_CALLTYPE lerpRows (const float* rowA, const float* rowB, float* dst, float t, int numPixels) noexcept; diff --git a/modules/yup_simd/types/yup_SIMDRegister.h b/modules/yup_simd/types/yup_SIMDRegister.h index fa187270f..237631023 100644 --- a/modules/yup_simd/types/yup_SIMDRegister.h +++ b/modules/yup_simd/types/yup_SIMDRegister.h @@ -238,6 +238,63 @@ class SIMDRegister return *this; } + //============================================================================== + /** Bitwise AND of two SIMD registers element-wise. + + @param other The SIMD register to AND with. + + @returns A SIMD register containing the element-wise bitwise AND. + */ + forcedinline SIMDRegister operator& (SIMDRegister other) const noexcept + { + SIMDRegister result; + for (std::size_t i = 0; i < numBatches; ++i) + result.data[i] = data[i] & other.data[i]; + return result; + } + + /** Bitwise OR of two SIMD registers element-wise. + + @param other The SIMD register to OR with. + + @returns A SIMD register containing the element-wise bitwise OR. + */ + forcedinline SIMDRegister operator| (SIMDRegister other) const noexcept + { + SIMDRegister result; + for (std::size_t i = 0; i < numBatches; ++i) + result.data[i] = data[i] | other.data[i]; + return result; + } + + /** Left shift each element by a scalar amount. + + @param shift The number of bits to shift left. + + @returns A SIMD register containing the shifted elements. + */ + forcedinline SIMDRegister operator<< (int shift) const noexcept + { + SIMDRegister result; + for (std::size_t i = 0; i < numBatches; ++i) + result.data[i] = data[i] << shift; + return result; + } + + /** Right shift each element by a scalar amount. + + @param shift The number of bits to shift right. + + @returns A SIMD register containing the shifted elements. + */ + forcedinline SIMDRegister operator>> (int shift) const noexcept + { + SIMDRegister result; + for (std::size_t i = 0; i < numBatches; ++i) + result.data[i] = data[i] >> shift; + return result; + } + //============================================================================== /** Performs a fused multiply-add operation: this + (a * b). @@ -382,9 +439,10 @@ class SIMDRegister std::array data; }; -using Float4 = SIMDRegister; -using Float8 = SIMDRegister; -using Double2 = SIMDRegister; -using Double4 = SIMDRegister; +using Float32x4 = SIMDRegister; +using Float32x8 = SIMDRegister; +using Float64x2 = SIMDRegister; +using Float64x4 = SIMDRegister; +using Uint32x4 = SIMDRegister; } // namespace yup diff --git a/modules/yup_simd/types/yup_Vec.h b/modules/yup_simd/types/yup_Vec.h index 6e6157fc8..735ff8ebc 100644 --- a/modules/yup_simd/types/yup_Vec.h +++ b/modules/yup_simd/types/yup_Vec.h @@ -86,21 +86,21 @@ struct alignas (16) Vec4f Vec4f operator+ (Vec4f other) const noexcept { alignas (16) float result[4]; - (Float4::loadUnaligned (data()) + Float4::loadUnaligned (other.data())).storeUnaligned (result); + (Float32x4::loadUnaligned (data()) + Float32x4::loadUnaligned (other.data())).storeUnaligned (result); return load (result); } Vec4f operator* (float scalar) const noexcept { alignas (16) float result[4]; - (Float4::loadUnaligned (data()) * Float4::broadcast (scalar)).storeUnaligned (result); + (Float32x4::loadUnaligned (data()) * Float32x4::broadcast (scalar)).storeUnaligned (result); return load (result); } Vec4f operator* (Vec4f other) const noexcept { alignas (16) float result[4]; - (Float4::loadUnaligned (data()) * Float4::loadUnaligned (other.data())).storeUnaligned (result); + (Float32x4::loadUnaligned (data()) * Float32x4::loadUnaligned (other.data())).storeUnaligned (result); return load (result); } diff --git a/modules/yup_simd/yup_simd.h b/modules/yup_simd/yup_simd.h index e34364d1e..69cd47f96 100644 --- a/modules/yup_simd/yup_simd.h +++ b/modules/yup_simd/yup_simd.h @@ -65,6 +65,8 @@ #include #include +//============================================================================== +// x86 / x64 SIMD feature detection //============================================================================== #ifndef YUP_USE_SSE_INTRINSICS #if defined(__SSE__) || defined(_M_X64) || defined(_M_AMD64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) @@ -72,12 +74,48 @@ #endif #endif +#ifndef YUP_USE_SSE2_INTRINSICS +#if defined(__SSE2__) || defined(YUP_USE_SSE_INTRINSICS) +#define YUP_USE_SSE2_INTRINSICS 1 +#endif +#endif + +#ifndef YUP_USE_SSE3_INTRINSICS +#if defined(__SSE3__) +#define YUP_USE_SSE3_INTRINSICS 1 +#endif +#endif + +#ifndef YUP_USE_SSSE3_INTRINSICS +#if defined(__SSSE3__) +#define YUP_USE_SSSE3_INTRINSICS 1 +#endif +#endif + +#ifndef YUP_USE_SSE4_1_INTRINSICS +#if defined(__SSE4_1__) +#define YUP_USE_SSE4_1_INTRINSICS 1 +#endif +#endif + +#ifndef YUP_USE_SSE4_2_INTRINSICS +#if defined(__SSE4_2__) +#define YUP_USE_SSE4_2_INTRINSICS 1 +#endif +#endif + #ifndef YUP_USE_AVX_INTRINSICS -#if defined(__AVX2__) +#if defined(__AVX__) #define YUP_USE_AVX_INTRINSICS 1 #endif #endif +#ifndef YUP_USE_AVX2_INTRINSICS +#if defined(__AVX2__) +#define YUP_USE_AVX2_INTRINSICS 1 +#endif +#endif + #ifndef YUP_USE_FMA_INTRINSICS #if defined(__FMA__) #define YUP_USE_FMA_INTRINSICS 1 @@ -86,10 +124,19 @@ #if ! YUP_INTEL #undef YUP_USE_SSE_INTRINSICS +#undef YUP_USE_SSE2_INTRINSICS +#undef YUP_USE_SSE3_INTRINSICS +#undef YUP_USE_SSSE3_INTRINSICS +#undef YUP_USE_SSE4_1_INTRINSICS +#undef YUP_USE_SSE4_2_INTRINSICS #undef YUP_USE_AVX_INTRINSICS +#undef YUP_USE_AVX2_INTRINSICS #undef YUP_USE_FMA_INTRINSICS #endif +//============================================================================== +// ARM SIMD feature detection +//============================================================================== #if __ARM_NEON__ && ! (YUP_USE_VDSP_FRAMEWORK || defined(YUP_USE_ARM_NEON)) #define YUP_USE_ARM_NEON 1 #endif @@ -101,6 +148,18 @@ #define YUP_USE_ARM_NEON 0 #endif +#ifndef YUP_USE_NEON64_INTRINSICS +#if defined(__aarch64__) +#define YUP_USE_NEON64_INTRINSICS 1 +#endif +#endif + +#ifndef YUP_USE_SVE_INTRINSICS +#if defined(__ARM_FEATURE_SVE) +#define YUP_USE_SVE_INTRINSICS 1 +#endif +#endif + //============================================================================== #if (YUP_MAC || YUP_IOS) && __has_include() #ifndef YUP_USE_VDSP_FRAMEWORK @@ -112,11 +171,21 @@ #endif //============================================================================== -#if YUP_USE_AVX_INTRINSICS || YUP_USE_FMA_INTRINSICS +// Intrinsics headers +//============================================================================== +#if YUP_USE_AVX_INTRINSICS || YUP_USE_AVX2_INTRINSICS || YUP_USE_FMA_INTRINSICS #include #endif -#if YUP_USE_SSE_INTRINSICS +#if YUP_USE_SSE4_2_INTRINSICS && ! (YUP_USE_AVX_INTRINSICS || YUP_USE_AVX2_INTRINSICS || YUP_USE_FMA_INTRINSICS) +#include +#elif YUP_USE_SSE4_1_INTRINSICS && ! (YUP_USE_AVX_INTRINSICS || YUP_USE_AVX2_INTRINSICS || YUP_USE_FMA_INTRINSICS) +#include +#elif YUP_USE_SSSE3_INTRINSICS && ! (YUP_USE_AVX_INTRINSICS || YUP_USE_AVX2_INTRINSICS || YUP_USE_FMA_INTRINSICS) +#include +#elif YUP_USE_SSE3_INTRINSICS && ! (YUP_USE_AVX_INTRINSICS || YUP_USE_AVX2_INTRINSICS || YUP_USE_FMA_INTRINSICS) +#include +#elif YUP_USE_SSE2_INTRINSICS && ! (YUP_USE_AVX_INTRINSICS || YUP_USE_AVX2_INTRINSICS || YUP_USE_FMA_INTRINSICS) #include #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a0a9ff7e7..c386d7cd8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -177,7 +177,7 @@ source_group (TREE ${CMAKE_CURRENT_LIST_DIR}/ FILES ${imported_sources}) set_source_files_properties (${imported_sources} PROPERTIES HEADER_FILE_ONLY TRUE) target_compile_options (${target_name} PRIVATE - $<$:-Wno-subobject-linkage>) + $<$,$>:-Wno-subobject-linkage>) set_target_properties (${target_name} PROPERTIES XCODE_SCHEME_ARGUMENTS "--gtest_filter=*" diff --git a/tests/main.cpp b/tests/main.cpp index da9861a0d..71d7304ad 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -106,6 +106,13 @@ struct TestApplication : yup::YUPApplication void initialise (const yup::String& commandLineParameters) override { + yup::SystemStats::setApplicationCrashHandler ([] (void*) + { + auto trace = yup::SystemStats::getStackBacktrace(); + std::fprintf (stderr, "\n=== CRASH ===\n%s\n=== END CRASH ===\n", trace.toRawUTF8()); + std::fflush (stderr); + }); + yup::Array argv; auto applicationName = getApplicationName(); diff --git a/tests/mocks/yup_graphics.h b/tests/mocks/yup_graphics.h index 99d80ca8d..cce50de72 100644 --- a/tests/mocks/yup_graphics.h +++ b/tests/mocks/yup_graphics.h @@ -31,7 +31,7 @@ // Test helper: Delegate GraphicsContext that allows injecting a mock ore context. // // Wraps a real (headless) GraphicsContext and delegates all methods to it except -// gpuContext(), which returns the supplied rive::ore::Context*. +// getGpuContext(), which returns the supplied rive::ore::Context*. // ============================================================================== class OreInjectedGraphicsContext : public yup::GraphicsContext @@ -47,11 +47,11 @@ class OreInjectedGraphicsContext : public yup::GraphicsContext yup::GpuDevice::Ptr getGpuDevice() const noexcept override { return real->getGpuDevice(); } - rive::Factory* factory() override { return real->factory(); } + rive::Factory* getFactory() override { return real->getFactory(); } - rive::gpu::RenderContext* renderContext() override { return real->renderContext(); } + rive::gpu::RenderContext* getRenderContext() override { return real->getRenderContext(); } - rive::gpu::RenderTarget* renderTarget() override { return real->renderTarget(); } + rive::gpu::RenderTarget* getRenderTarget() override { return real->getRenderTarget(); } std::unique_ptr makeRenderer (int width, int height) override { return real->makeRenderer (width, height); } diff --git a/tests/mocks/yup_rhi.h b/tests/mocks/yup_rhi.h index 87843908c..33b2e1fce 100644 --- a/tests/mocks/yup_rhi.h +++ b/tests/mocks/yup_rhi.h @@ -29,7 +29,7 @@ // Test helper: Delegate GpuDevice that allows injecting a mock ore context. // // Wraps a real (headless) GpuDevice and delegates all methods to it except -// gpuContext(), which returns the supplied rive::ore::Context*. +// getGpuContext(), which returns the supplied rive::ore::Context*. // ============================================================================== class OreInjectedGpuDevice : public yup::GpuDevice @@ -43,7 +43,7 @@ class OreInjectedGpuDevice : public yup::GpuDevice yup::GpuPlatform getPlatform() const noexcept override { return real->getPlatform(); } - rive::ore::Context* gpuContext() const noexcept override { return injectedOreContext; } + rive::ore::Context* getGpuContext() const noexcept override { return injectedOreContext; } std::unique_ptr createOffscreenTarget (int width, int height) override { return real->createOffscreenTarget (width, height); } diff --git a/tests/yup_graphics/yup_GifImageFormat.cpp b/tests/yup_graphics/yup_GifImageFormat.cpp index 6cc0dbd74..70d67d73a 100644 --- a/tests/yup_graphics/yup_GifImageFormat.cpp +++ b/tests/yup_graphics/yup_GifImageFormat.cpp @@ -23,8 +23,6 @@ #include "yup_ImageFormatTools.h" -#if YUP_MODULE_AVAILABLE_libgif && YUP_IMAGE_FORMAT_GIF - //============================================================================== // Helpers //============================================================================== @@ -1162,5 +1160,3 @@ TEST (GifImageFormatTests, WriteImageReturnsFalseForInvalidImage) Image invalid; EXPECT_FALSE (writer.writeImage (invalid)); } - -#endif // YUP_MODULE_AVAILABLE_libgif && YUP_IMAGE_FORMAT_GIF diff --git a/tests/yup_graphics/yup_Image.cpp b/tests/yup_graphics/yup_Image.cpp index 9ba1e3939..106062adc 100644 --- a/tests/yup_graphics/yup_Image.cpp +++ b/tests/yup_graphics/yup_Image.cpp @@ -88,13 +88,12 @@ TEST (ImageTests, GrayscaleBitmapConvertsToOpaqueRGBATextureBytes) raw[1] = 127; raw[2] = 255; - uint8 textureBytes[12] = {}; + uint32_t textureBytes[3] = {}; ColorVectorOperations::convertGrayscaleToRGBA (raw.data(), textureBytes, 3); - const uint8 expected[] = { 0, 0, 0, 255, 127, 127, 127, 255, 255, 255, 255, 255 }; - - for (size_t i = 0; i < std::size (expected); ++i) - EXPECT_EQ (textureBytes[i], expected[i]); + EXPECT_EQ (textureBytes[0], 0xff000000u); + EXPECT_EQ (textureBytes[1], 0xff7f7f7fu); + EXPECT_EQ (textureBytes[2], 0xffffffffu); } TEST (ImageTests, RgbBitmapConvertsToOpaqueRGBATextureBytes) @@ -105,13 +104,11 @@ TEST (ImageTests, RgbBitmapConvertsToOpaqueRGBATextureBytes) image.setPixel (1, 0, 0xffabcdef); const auto raw = image.getRawData(); - uint8 textureBytes[8] = {}; + uint32_t textureBytes[2] = {}; ColorVectorOperations::convertRGBToRGBA (raw.data(), textureBytes, 2); - const uint8 expected[] = { 0x12, 0x34, 0x56, 0xff, 0xab, 0xcd, 0xef, 0xff }; - - for (size_t i = 0; i < std::size (expected); ++i) - EXPECT_EQ (textureBytes[i], expected[i]); + EXPECT_EQ (textureBytes[0], 0xff563412u); + EXPECT_EQ (textureBytes[1], 0xffefcdabu); } TEST (ImageTests, RgbaBitmapConvertsToPremultipliedRGBATextureBytes) @@ -122,14 +119,12 @@ TEST (ImageTests, RgbaBitmapConvertsToPremultipliedRGBATextureBytes) image.setPixel (1, 0, 0xff010203); const auto raw = image.getRawData(); - uint8 textureBytes[8] = {}; + uint32_t textureBytes[2] = {}; std::memcpy (textureBytes, raw.data(), raw.size()); ColorVectorOperations::premultiplyRGBA (textureBytes, 2); - const uint8 expected[] = { 8, 16, 32, 128, 1, 2, 3, 255 }; - - for (size_t i = 0; i < std::size (expected); ++i) - EXPECT_EQ (textureBytes[i], expected[i]); + EXPECT_EQ (textureBytes[0], 0x80201008u); + EXPECT_EQ (textureBytes[1], 0xff030201u); } TEST (ImageTests, ColorCanConvertToExplicitPackedByteOrders) diff --git a/tests/yup_graphics/yup_JpegImageFormat.cpp b/tests/yup_graphics/yup_JpegImageFormat.cpp index 63059840a..342ac9c14 100644 --- a/tests/yup_graphics/yup_JpegImageFormat.cpp +++ b/tests/yup_graphics/yup_JpegImageFormat.cpp @@ -23,8 +23,6 @@ #include "yup_ImageFormatTools.h" -#if YUP_MODULE_AVAILABLE_libjpeg && YUP_IMAGE_FORMAT_JPEG - // ====================================================================== // Reader dimension and header tests // ====================================================================== @@ -756,5 +754,3 @@ TEST (JpegImageFormatTests, MetadataExtractsExifOrientation) ASSERT_NE (reader.metadata, nullptr); EXPECT_EQ (reader.metadata->getOrientation(), 6); } - -#endif // YUP_MODULE_AVAILABLE_libjpeg && YUP_IMAGE_FORMAT_JPEG diff --git a/tests/yup_graphics/yup_PngImageFormat.cpp b/tests/yup_graphics/yup_PngImageFormat.cpp index b6daeb6a8..b1bad33b0 100644 --- a/tests/yup_graphics/yup_PngImageFormat.cpp +++ b/tests/yup_graphics/yup_PngImageFormat.cpp @@ -23,8 +23,6 @@ #include "yup_ImageFormatTools.h" -#if YUP_MODULE_AVAILABLE_libpng && YUP_IMAGE_FORMAT_PNG - // ====================================================================== // Reader dimension and header tests // ====================================================================== @@ -766,5 +764,3 @@ TEST (PngImageFormatTests, ParseMetadataExtractsTextChunks) EXPECT_EQ (reader.metadata->textEntries.getValue ("Title", {}), String ("Test PNG")); EXPECT_EQ (reader.metadata->textEntries.getValue ("Author", {}), String ("YUP")); } - -#endif // YUP_MODULE_AVAILABLE_libpng && YUP_IMAGE_FORMAT_PNG diff --git a/tests/yup_graphics/yup_TiffImageFormat.cpp b/tests/yup_graphics/yup_TiffImageFormat.cpp index 150a416f1..aece94045 100644 --- a/tests/yup_graphics/yup_TiffImageFormat.cpp +++ b/tests/yup_graphics/yup_TiffImageFormat.cpp @@ -23,8 +23,6 @@ #include "yup_ImageFormatTools.h" -#if YUP_MODULE_AVAILABLE_libtiff && YUP_IMAGE_FORMAT_TIFF - using namespace yup; // ====================================================================== @@ -871,5 +869,3 @@ TEST (TiffImageFormatTests, ParseRawChunksExtractsExifWhenPresent) ASSERT_NE (reader.metadata, nullptr); EXPECT_FALSE (reader.metadata->hasRawChunk ("tiff/exif")); } - -#endif // YUP_MODULE_AVAILABLE_libtiff && YUP_IMAGE_FORMAT_TIFF diff --git a/tests/yup_graphics/yup_WebPImageFormat.cpp b/tests/yup_graphics/yup_WebPImageFormat.cpp index fa073b8e8..c3aa5fb0e 100644 --- a/tests/yup_graphics/yup_WebPImageFormat.cpp +++ b/tests/yup_graphics/yup_WebPImageFormat.cpp @@ -23,8 +23,6 @@ #include "yup_ImageFormatTools.h" -#if YUP_MODULE_AVAILABLE_libwebp && YUP_IMAGE_FORMAT_WEBP - // ====================================================================== // Reader dimension and header tests // ====================================================================== @@ -852,5 +850,3 @@ TEST (WebPImageFormatTests, ParseRawChunksCreatesMetadata) EXPECT_DOUBLE_EQ (reader.metadata->dpiX, 0.0); EXPECT_DOUBLE_EQ (reader.metadata->dpiY, 0.0); } - -#endif // YUP_MODULE_AVAILABLE_libwebp && YUP_IMAGE_FORMAT_WEBP diff --git a/tests/yup_gui/yup_PopupMenu.cpp b/tests/yup_gui/yup_PopupMenu.cpp index 30ac91c74..4c66a3138 100644 --- a/tests/yup_gui/yup_PopupMenu.cpp +++ b/tests/yup_gui/yup_PopupMenu.cpp @@ -53,6 +53,11 @@ class PopupMenuTest : public ::testing::Test parentComponent->addAndMakeVisible (*targetComponent); } + void TearDown() override + { + PopupMenu::dismissAllPopups(); + } + std::unique_ptr parentComponent; std::unique_ptr targetComponent; }; diff --git a/tests/yup_rhi.cpp b/tests/yup_rhi.cpp index f45c0d407..13ec6e121 100644 --- a/tests/yup_rhi.cpp +++ b/tests/yup_rhi.cpp @@ -27,3 +27,7 @@ #include "yup_rhi/yup_GpuTarget.cpp" #include "yup_rhi/yup_GpuPipeline.cpp" #include "yup_rhi/yup_GpuPipelineMocked.cpp" + +#if YUP_LINUX +#include "yup_rhi/native/yup_GpuDevice_linux.cpp" +#endif diff --git a/tests/yup_rhi/native/yup_GpuDevice_linux.cpp b/tests/yup_rhi/native/yup_GpuDevice_linux.cpp new file mode 100644 index 000000000..b04767bfe --- /dev/null +++ b/tests/yup_rhi/native/yup_GpuDevice_linux.cpp @@ -0,0 +1,518 @@ +/* + ============================================================================== + + This file is part of the YUP library. + Copyright (c) 2026 - kunitoki@gmail.com + + YUP is an open source library subject to open-source licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + to use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + YUP IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include + +#include +#include +#include + +#include + +#include + +using namespace yup; + +namespace +{ + +// ============================================================================== +// Helper: create a GpuDevice backed by a real OpenGL context via SDL. +// +// Creates a hidden SDL window + GL context, makes it current, and uses +// SDL_GL_GetProcAddress as the loader function for GpuDevice::Options. +// ============================================================================== + +struct GLContext +{ + SDL_Window* window = nullptr; + SDL_GLContext glContext = nullptr; + + bool init() + { +#if YUP_LINUX + // Force Mesa's llvmpipe software rasterizer for CI environments. + // Real GPU drivers take precedence when available. + setenv ("LIBGL_ALWAYS_SOFTWARE", "1", 0); + setenv ("GALLIUM_DRIVER", "llvmpipe", 0); +#endif + + SDL_SetHint (SDL_HINT_RENDER_DRIVER, "opengl"); + + SDL_GL_SetAttribute (SDL_GL_CONTEXT_MAJOR_VERSION, YUP_RIVE_OPENGL_MAJOR); + SDL_GL_SetAttribute (SDL_GL_CONTEXT_MINOR_VERSION, YUP_RIVE_OPENGL_MINOR); + SDL_GL_SetAttribute (SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + window = SDL_CreateWindow ("yup_rhi_gl_test", + 64, + 64, + SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + if (window == nullptr) + { + fprintf (stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError()); + return false; + } + + glContext = SDL_GL_CreateContext (window); + if (glContext == nullptr) + { + fprintf (stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError()); + SDL_DestroyWindow (window); + window = nullptr; + return false; + } + + SDL_GL_MakeCurrent (window, glContext); + return true; + } + + void shutdown() + { + if (glContext != nullptr) + { + SDL_GL_DestroyContext (glContext); + glContext = nullptr; + } + if (window != nullptr) + { + SDL_DestroyWindow (window); + window = nullptr; + } + } + + GpuDevice::Ptr createDevice() const + { + GpuDevice::Options opts; + opts.loaderFunction = (GpuDevice::LoaderFunction) SDL_GL_GetProcAddress; + opts.readableFramebuffer = true; + + auto device = GpuDevice::create (GpuPlatform::OpenGL, opts); + if (device == nullptr) + fprintf (stderr, "GpuDevice::create(OpenGL) returned null\n"); + + return device; + } +}; + +} // namespace + +// ============================================================================== +// GpuDeviceOpenGL — real GPU device tests (Linux, OpenGL via SDL) +// ============================================================================== + +class GpuDeviceOpenGLTests : public ::testing::Test +{ +protected: + void SetUp() override + { + if (! gl.init()) + GTEST_SKIP() << "Cannot create OpenGL context — is a display available?"; + device = gl.createDevice(); + if (device == nullptr) + GTEST_SKIP() << "Cannot create OpenGL GpuDevice — is GL 4.5 supported?"; + + GpuDevice::Options ctxOpts; + ctxOpts.loaderFunction = (GpuDevice::LoaderFunction) SDL_GL_GetProcAddress; + graphicsContext = GraphicsContext::createContext (GpuPlatform::OpenGL, ctxOpts, device); + if (graphicsContext == nullptr) + GTEST_SKIP() << "Cannot create OpenGL GraphicsContext"; + } + + void TearDown() override + { + graphicsContext = nullptr; + device = nullptr; + gl.shutdown(); + } + + GLContext gl; + GpuDevice::Ptr device; + std::unique_ptr graphicsContext; +}; + +// -------------------------------------------------------------------------- +// Basic device creation +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, DeviceIsNotNull) +{ + ASSERT_NE (device, nullptr); +} + +TEST_F (GpuDeviceOpenGLTests, PlatformIsOpenGL) +{ + EXPECT_EQ (device->getPlatform(), GpuPlatform::OpenGL); +} + +TEST_F (GpuDeviceOpenGLTests, GpuContextIsNotNull) +{ + EXPECT_NE (device->getGpuContext(), nullptr); +} + +// -------------------------------------------------------------------------- +// Buffer creation +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, CreateVertexBuffer) +{ + const float verts[] = { 0.0f, 1.0f, 2.0f, 3.0f }; + auto buf = device->createBuffer (GpuBufferType::vertex, verts, sizeof (verts)); + ASSERT_NE (buf, nullptr); + EXPECT_EQ (buf->getType(), GpuBufferType::vertex); + EXPECT_EQ (buf->getSizeInBytes(), sizeof (verts)); + EXPECT_TRUE (buf->isValid()); +} + +TEST_F (GpuDeviceOpenGLTests, CreateIndexBuffer) +{ + const uint16_t indices[] = { 0, 1, 2, 3 }; + auto buf = device->createBuffer (GpuBufferType::index, indices, sizeof (indices)); + ASSERT_NE (buf, nullptr); + EXPECT_EQ (buf->getType(), GpuBufferType::index); + EXPECT_TRUE (buf->isValid()); +} + +TEST_F (GpuDeviceOpenGLTests, CreateUniformBuffer) +{ + const int data[] = { 42, 43, 44 }; + auto buf = device->createBuffer (GpuBufferType::uniform, data, sizeof (data)); + ASSERT_NE (buf, nullptr); + EXPECT_EQ (buf->getType(), GpuBufferType::uniform); + EXPECT_TRUE (buf->isValid()); +} + +TEST_F (GpuDeviceOpenGLTests, CreateBufferWithNullDataReturnsNull) +{ + EXPECT_EQ (device->createBuffer (GpuBufferType::vertex, nullptr, 16), nullptr); +} + +TEST_F (GpuDeviceOpenGLTests, CreateBufferWithZeroSizeReturnsNull) +{ + const float data[] = { 1.0f }; + EXPECT_EQ (device->createBuffer (GpuBufferType::vertex, data, 0), nullptr); +} + +// -------------------------------------------------------------------------- +// Buffer update +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, UpdateVertexBuffer) +{ + const float data[] = { 1.0f, 2.0f, 3.0f, 4.0f }; + auto buf = device->createBuffer (GpuBufferType::vertex, data, sizeof (data)); + ASSERT_NE (buf, nullptr); + + const float newData[] = { 5.0f, 6.0f, 7.0f, 8.0f }; + EXPECT_TRUE (device->updateBuffer (buf, newData, sizeof (newData))); +} + +TEST_F (GpuDeviceOpenGLTests, UpdateBufferLargerThanOriginalReturnsFalse) +{ + const float data[] = { 1.0f }; + auto buf = device->createBuffer (GpuBufferType::vertex, data, sizeof (data)); + ASSERT_NE (buf, nullptr); + + const float larger[] = { 1.0f, 2.0f, 3.0f, 4.0f }; + EXPECT_FALSE (device->updateBuffer (buf, larger, sizeof (larger))); +} + +// -------------------------------------------------------------------------- +// Offscreen target creation +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, CreateOffscreenTarget) +{ + auto target = device->createOffscreenTarget (256, 256); + ASSERT_NE (target, nullptr); + EXPECT_EQ (target->getWidth(), 256); + EXPECT_EQ (target->getHeight(), 256); +} + +TEST_F (GpuDeviceOpenGLTests, CreateOffscreenTargetZeroSize) +{ + EXPECT_EQ (device->createOffscreenTarget (0, 256), nullptr); + EXPECT_EQ (device->createOffscreenTarget (256, 0), nullptr); +} + +// -------------------------------------------------------------------------- +// GpuTarget +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, GpuTargetCreate) +{ + auto target = GpuTarget::create (device, 128, 128); + ASSERT_NE (target, nullptr); + EXPECT_EQ (target->getWidth(), 128); + EXPECT_EQ (target->getHeight(), 128); +} + +TEST_F (GpuDeviceOpenGLTests, GpuTargetAsTexture) +{ + auto target = GpuTarget::create (device, 128, 128); + ASSERT_NE (target, nullptr); + + auto tex = target->asTexture(); + ASSERT_NE (tex, nullptr); + EXPECT_TRUE (tex->isValid()); + EXPECT_EQ (tex->getWidth(), 128); + EXPECT_EQ (tex->getHeight(), 128); + EXPECT_TRUE (tex->isRenderTarget()); +} + +TEST_F (GpuDeviceOpenGLTests, GpuTargetReadPixels) +{ + auto target = GpuTarget::create (device, 128, 128); + ASSERT_NE (target, nullptr); + + std::vector pixels (128 * 128 * 4); + EXPECT_TRUE (target->readPixels (pixels.data(), pixels.size())); +} + +TEST_F (GpuDeviceOpenGLTests, GpuTargetReadPixelsTooSmallBufferReturnsFalse) +{ + auto target = GpuTarget::create (device, 128, 128); + ASSERT_NE (target, nullptr); + + std::vector pixels (16); + EXPECT_FALSE (target->readPixels (pixels.data(), pixels.size())); +} + +// -------------------------------------------------------------------------- +// GpuFrame +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, GpuFrameBeginReturnsValidFrame) +{ + auto frame = GpuFrame::begin (device); + EXPECT_TRUE (frame.isValid()); +} + +TEST_F (GpuDeviceOpenGLTests, GpuFrameSubmit) +{ + auto frame = GpuFrame::begin (device); + ASSERT_TRUE (frame.isValid()); + EXPECT_TRUE (frame.submit()); + EXPECT_FALSE (frame.submit()); // Idempotent after first submit. +} + +TEST_F (GpuDeviceOpenGLTests, GpuFrameWaitForGPU) +{ + auto frame = GpuFrame::begin (device); + ASSERT_TRUE (frame.isValid()); + frame.submit(); + EXPECT_NO_THROW (frame.waitForGPU()); +} + +// -------------------------------------------------------------------------- +// Pipeline compilation +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, CompilePipelineWithMinimalShaders) +{ +#if ! YUP_ENABLE_SHADER_TRANSPILER + GTEST_SKIP() << "Shader transpiler unavailable — cannot compile GLSL sources inline"; +#else + const char* vsSrc = R"( + #version 450 + layout(set = 0, binding = 0) uniform Uniforms { mat4 mvp; } ubo; + layout(location = 0) in vec3 aPos; + void main() { gl_Position = ubo.mvp * vec4(aPos, 1.0); } + )"; + + const char* fsSrc = R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { fragColor = vec4(1.0, 0.0, 0.0, 1.0); } + )"; + + auto result = GpuPipeline::compileFromGlsl (device, vsSrc, fsSrc); + ASSERT_TRUE (result.wasOk()); + ASSERT_NE (result.getValue(), nullptr); +#endif +} + +TEST_F (GpuDeviceOpenGLTests, CompilePipelineFailsWithEmptyVertexCode) +{ + GpuShaderSource vs; + vs.language = GpuShaderLanguage::glsl; + vs.code = nullptr; + vs.codeSize = 0; + + GpuShaderSource fs; + fs.language = GpuShaderLanguage::glsl; + fs.code = "void main() {}"; + fs.codeSize = (uint32_t) strlen ("void main() {}"); + + auto result = GpuPipeline::compile (device, vs, fs); + EXPECT_TRUE (result.failed()); +} + +// -------------------------------------------------------------------------- +// Render pass (end-to-end) +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, RenderPassDrawTriangle) +{ + // Create a render target. + auto target = GpuTarget::create (device, 256, 256); + ASSERT_NE (target, nullptr); + +#if ! YUP_ENABLE_SHADER_TRANSPILER + GTEST_SKIP() << "Shader transpiler unavailable — cannot compile GLSL sources inline"; +#else + // Compile a minimal pipeline. + const char* vsSrc = R"( + #version 450 + layout(set = 0, binding = 0) uniform Uniforms { mat4 mvp; } ubo; + layout(location = 0) in vec3 aPos; + void main() { gl_Position = ubo.mvp * vec4(aPos, 1.0); } + )"; + + const char* fsSrc = R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { fragColor = vec4(1.0, 0.0, 0.0, 1.0); } + )"; + + auto compileResult = GpuPipeline::compileFromGlsl (device, vsSrc, fsSrc); + ASSERT_TRUE (compileResult.wasOk()); + auto* pipeline = compileResult.getValue().get(); + ASSERT_NE (pipeline, nullptr); + + // Begin a frame and render pass. + auto frame = GpuFrame::begin (device); + ASSERT_TRUE (frame.isValid()); + + auto pass = target->beginRenderPass (frame, { true, Color (0, 0, 0, 0) }); + ASSERT_TRUE (pass.isValid()); + + pass.setPipeline (*pipeline); + EXPECT_TRUE (pass.draw (3)); + + pass.finish(); + frame.submit(); +#endif +} + +TEST_F (GpuDeviceOpenGLTests, RenderPassClearColor) +{ + auto target = GpuTarget::create (device, 64, 64); + ASSERT_NE (target, nullptr); + + // Render with a green clear color. + auto frame = GpuFrame::begin (device); + ASSERT_TRUE (frame.isValid()); + + GpuRenderOptions opts; + opts.clear = true; + opts.clearColor = Color (255, 0, 255, 0); // ARGB: Green, fully opaque. + + auto pass = target->beginRenderPass (frame, opts); + ASSERT_TRUE (pass.isValid()); + pass.finish(); + frame.submit(); + frame.waitForGPU(); + + // Read back the pixels — they should be green. + std::vector pixels (64 * 64 * 4); + ASSERT_TRUE (target->readPixels (pixels.data(), pixels.size())); + + // Check a few pixels in the center are green. + const size_t center = (32 * 64 + 32) * 4; + EXPECT_EQ (pixels[center + 0], 0u); // R + EXPECT_EQ (pixels[center + 1], 255u); // G + EXPECT_EQ (pixels[center + 2], 0u); // B + EXPECT_EQ (pixels[center + 3], 255u); // A +} + +TEST_F (GpuDeviceOpenGLTests, GpuFrameMovePreservesState) +{ + auto src = GpuFrame::begin (device); + ASSERT_TRUE (src.isValid()); + + GpuFrame dst (std::move (src)); + EXPECT_TRUE (dst.isValid()); + EXPECT_FALSE (src.isValid()); + + dst.submit(); +} + +// -------------------------------------------------------------------------- +// GpuCanvas integration (via GpuCanvas, which wraps GpuTarget) +// -------------------------------------------------------------------------- + +TEST_F (GpuDeviceOpenGLTests, GpuCanvasCreate) +{ + auto canvas = GpuCanvas::create (*graphicsContext, 256, 256); + ASSERT_NE (canvas, nullptr); +} + +TEST_F (GpuDeviceOpenGLTests, GpuCanvasBeginDrawAndCommit) +{ + auto canvas = GpuCanvas::create (*graphicsContext, 256, 256); + ASSERT_NE (canvas, nullptr); + + canvas->beginDraw(); + canvas->commit(); +} + +TEST_F (GpuDeviceOpenGLTests, GpuCanvasAsImage) +{ + auto canvas = GpuCanvas::create (*graphicsContext, 256, 256); + ASSERT_NE (canvas, nullptr); + + auto& g = canvas->beginDraw(); + + // Draw a filled rectangle. + g.setFillColor (Color (255, 255, 0, 0)); // ARGB: Red, fully opaque. + g.fillRect (0, 0, 256, 256); + + canvas->commit(); + + auto img = canvas->asImage(); + EXPECT_TRUE (img.isValid()); + EXPECT_EQ (img.getWidth(), 256); + EXPECT_EQ (img.getHeight(), 256); +} + +TEST_F (GpuDeviceOpenGLTests, GpuCanvasReadPixelsAfterDraw) +{ + auto canvas = GpuCanvas::create (*graphicsContext, 128, 128); + ASSERT_NE (canvas, nullptr); + + auto& g = canvas->beginDraw(); + + // Fill the entire canvas with red. + g.setFillColor (Color (255, 255, 0, 0)); // ARGB: Red, fully opaque. + g.fillRect (0, 0, 128, 128); + + canvas->commit(); + + std::vector pixels (128 * 128 * 4); + EXPECT_TRUE (canvas->readPixels (pixels.data(), pixels.size())); + + // Center pixel should be red. + const size_t centerIdx = (64 * 128 + 64) * 4; + EXPECT_EQ (pixels[centerIdx + 0], 255u); // R + EXPECT_EQ (pixels[centerIdx + 1], 0u); // G + EXPECT_EQ (pixels[centerIdx + 2], 0u); // B + EXPECT_EQ (pixels[centerIdx + 3], 255u); // A +} diff --git a/tests/yup_simd/yup_ColorVectorOperations.cpp b/tests/yup_simd/yup_ColorVectorOperations.cpp index 8e0a24280..3776a9b23 100644 --- a/tests/yup_simd/yup_ColorVectorOperations.cpp +++ b/tests/yup_simd/yup_ColorVectorOperations.cpp @@ -25,6 +25,25 @@ using namespace yup; +namespace +{ +constexpr uint32_t makeRGBA (uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept +{ + return static_cast (r) + | (static_cast (g) << 8) + | (static_cast (b) << 16) + | (static_cast (a) << 24); +} + +constexpr uint32_t makeBGRA (uint8_t b, uint8_t g, uint8_t r, uint8_t a) noexcept +{ + return static_cast (b) + | (static_cast (g) << 8) + | (static_cast (r) << 16) + | (static_cast (a) << 24); +} +} // namespace + // ============================================================================== // premultiplyARGB tests // ============================================================================== @@ -83,55 +102,159 @@ TEST (ColorVectorOpsTests, PremultiplyARGBZeroPixels) EXPECT_EQ (pixels[0], 0xdeadbeefu); } +TEST (ColorVectorOpsTests, PremultiplyARGBLargePixelCount) +{ + // Exercise SIMD path with 100 pixels — well past the scalar tail boundary. + constexpr int kCount = 100; + uint32_t pixels[kCount]; + for (int i = 0; i < kCount; ++i) + { + const uint32_t r = (uint32_t) ((i * 3) & 0xff); + const uint32_t g = (uint32_t) ((i * 5) & 0xff); + const uint32_t b = (uint32_t) ((i * 7) & 0xff); + const uint32_t a = (uint32_t) ((i * 11) & 0xff); + pixels[i] = (a << 24) | (r << 16) | (g << 8) | b; + } + + ColorVectorOperations::premultiplyARGB (pixels, kCount); + + for (int i = 0; i < kCount; ++i) + { + const uint32_t a = (uint32_t) ((i * 11) & 0xff); + const uint32_t expectedR = ((uint32_t) ((i * 3) & 0xff) * a + 127u) / 255u; + const uint32_t expectedG = ((uint32_t) ((i * 5) & 0xff) * a + 127u) / 255u; + const uint32_t expectedB = ((uint32_t) ((i * 7) & 0xff) * a + 127u) / 255u; + + EXPECT_EQ ((pixels[i] >> 24) & 0xffu, a); + EXPECT_EQ ((pixels[i] >> 16) & 0xffu, expectedR); + EXPECT_EQ ((pixels[i] >> 8) & 0xffu, expectedG); + EXPECT_EQ (pixels[i] & 0xffu, expectedB); + } +} + +TEST (ColorVectorOpsTests, PremultiplyARGBOddPixelCount) +{ + // 5 pixels — not a multiple of 4, exercises both SIMD and scalar tail. + uint32_t pixels[] = { + (0x80u << 24) | (0xffu << 16) | (0x80u << 8) | 0x80u, + (0x40u << 24) | (0x40u << 16) | (0x40u << 8) | 0x40u, + (0xffu << 24) | (0x10u << 16) | (0x20u << 8) | 0x30u, + (0x00u << 24) | (0xaau << 16) | (0xbbu << 8) | 0xccu, + (0xc0u << 24) | (0x22u << 16) | (0x44u << 8) | 0x66u + }; + + ColorVectorOperations::premultiplyARGB (pixels, 5); + + for (int i = 0; i < 5; ++i) + { + const uint32_t a = (pixels[i] >> 24) & 0xffu; + const uint32_t r = (pixels[i] >> 16) & 0xffu; + const uint32_t g = (pixels[i] >> 8) & 0xffu; + const uint32_t b = pixels[i] & 0xffu; + + // Verify premultiplied channel ≤ min(original channel, alpha) + EXPECT_LE (r, a); + EXPECT_LE (g, a); + EXPECT_LE (b, a); + } +} + // ============================================================================== // premultiplyRGBA tests // ============================================================================== TEST (ColorVectorOpsTests, PremultiplyRGBAMatchesScalarReference) { - uint8 pixels[] = { 255, 128, 64, 128, 1, 2, 3, 255, 1, 2, 3, 0 }; + // RGBA pixels as uint32: [R,G,B,A] with premultiply + uint32_t pixels[] = { + makeRGBA (255, 128, 64, 128), + makeRGBA (1, 2, 3, 255), + makeRGBA (1, 2, 3, 0) + }; ColorVectorOperations::premultiplyRGBA (pixels, 3); - EXPECT_EQ (pixels[0], 128); - EXPECT_EQ (pixels[1], 64); - EXPECT_EQ (pixels[2], 32); - EXPECT_EQ (pixels[3], 128); - EXPECT_EQ (pixels[4], 1); - EXPECT_EQ (pixels[5], 2); - EXPECT_EQ (pixels[6], 3); - EXPECT_EQ (pixels[7], 255); - EXPECT_EQ (pixels[8], 0); - EXPECT_EQ (pixels[9], 0); - EXPECT_EQ (pixels[10], 0); - EXPECT_EQ (pixels[11], 0); + EXPECT_EQ (pixels[0], makeRGBA (128, 64, 32, 128)); + EXPECT_EQ (pixels[1], makeRGBA (1, 2, 3, 255)); + EXPECT_EQ (pixels[2], 0x00000000u); } TEST (ColorVectorOpsTests, PremultiplyRGBAFullAlphaUnchanged) { - uint8 pixels[] = { 100, 150, 200, 255 }; + uint32_t pixels[] = { makeRGBA (100, 150, 200, 255) }; ColorVectorOperations::premultiplyRGBA (pixels, 1); - EXPECT_EQ (pixels[0], 100); - EXPECT_EQ (pixels[1], 150); - EXPECT_EQ (pixels[2], 200); - EXPECT_EQ (pixels[3], 255); + EXPECT_EQ (pixels[0], makeRGBA (100, 150, 200, 255)); } TEST (ColorVectorOpsTests, PremultiplyRGBAZeroAlphaBlacksOut) { - uint8 pixels[] = { 100, 150, 200, 0 }; + uint32_t pixels[] = { makeRGBA (100, 150, 200, 0) }; ColorVectorOperations::premultiplyRGBA (pixels, 1); - EXPECT_EQ (pixels[0], 0); - EXPECT_EQ (pixels[1], 0); - EXPECT_EQ (pixels[2], 0); - EXPECT_EQ (pixels[3], 0); + EXPECT_EQ (pixels[0], 0x00000000u); } TEST (ColorVectorOpsTests, PremultiplyRGBAZeroPixels) { - uint8 pixels[] = { 99, 98, 97, 96 }; + uint32_t pixels[] = { makeRGBA (99, 98, 97, 96) }; ColorVectorOperations::premultiplyRGBA (pixels, 0); - EXPECT_EQ (pixels[0], 99); + EXPECT_EQ (pixels[0], makeRGBA (99, 98, 97, 96)); +} + +TEST (ColorVectorOpsTests, PremultiplyRGBALargePixelCount) +{ + // Exercise SIMD path with 100 pixels — well past the scalar tail boundary. + constexpr int kCount = 100; + uint32_t pixels[kCount]; + for (int i = 0; i < kCount; ++i) + { + const uint8_t r = (uint8_t) ((i * 3) & 0xff); + const uint8_t g = (uint8_t) ((i * 5) & 0xff); + const uint8_t b = (uint8_t) ((i * 7) & 0xff); + const uint8_t a = (uint8_t) ((i * 11) & 0xff); + pixels[i] = makeRGBA (r, g, b, a); + } + + ColorVectorOperations::premultiplyRGBA (pixels, kCount); + + for (int i = 0; i < kCount; ++i) + { + const uint32_t a = (uint32_t) ((i * 11) & 0xff); + const uint32_t expectedR = ((uint32_t) ((i * 3) & 0xff) * a + 127u) / 255u; + const uint32_t expectedG = ((uint32_t) ((i * 5) & 0xff) * a + 127u) / 255u; + const uint32_t expectedB = ((uint32_t) ((i * 7) & 0xff) * a + 127u) / 255u; + + EXPECT_EQ ((pixels[i] >> 24) & 0xffu, a); + EXPECT_EQ (pixels[i] & 0xffu, expectedR); + EXPECT_EQ ((pixels[i] >> 8) & 0xffu, expectedG); + EXPECT_EQ ((pixels[i] >> 16) & 0xffu, expectedB); + } +} + +TEST (ColorVectorOpsTests, PremultiplyRGBAOddPixelCount) +{ + // 5 pixels — not a multiple of 4, exercises both SIMD and scalar tail. + uint32_t pixels[] = { + makeRGBA (255, 128, 64, 128), + makeRGBA (64, 64, 64, 64), + makeRGBA (16, 32, 48, 255), + makeRGBA (170, 187, 204, 0), + makeRGBA (34, 68, 102, 192) + }; + + ColorVectorOperations::premultiplyRGBA (pixels, 5); + + for (int i = 0; i < 5; ++i) + { + const uint32_t a = (pixels[i] >> 24) & 0xffu; + const uint32_t r = pixels[i] & 0xffu; + const uint32_t g = (pixels[i] >> 8) & 0xffu; + const uint32_t b = (pixels[i] >> 16) & 0xffu; + + // Verify premultiplied channel ≤ min(original channel, alpha) + EXPECT_LE (r, a); + EXPECT_LE (g, a); + EXPECT_LE (b, a); + } } // ============================================================================== @@ -192,48 +315,80 @@ TEST (ColorVectorOpsTests, ConvertARGBtoRGBAAllOnes) EXPECT_EQ (rgba[0], 0xffffffffu); } +TEST (ColorVectorOpsTests, ConvertARGBtoRGBALargePixelCount) +{ + // Exercise SIMD path with 100 pixels — well past the scalar tail boundary. + constexpr int kCount = 100; + uint32_t argb[kCount]; + for (int i = 0; i < kCount; ++i) + { + const uint32_t a = (uint32_t) ((i * 7) & 0xff); + const uint32_t r = (uint32_t) ((i * 3) & 0xff); + const uint32_t g = (uint32_t) ((i * 5) & 0xff); + const uint32_t b = (uint32_t) (i & 0xff); + argb[i] = (a << 24) | (r << 16) | (g << 8) | b; + } + + uint32_t rgba[kCount] = {}; + ColorVectorOperations::convertARGBtoRGBA (argb, rgba, kCount); + + for (int i = 0; i < kCount; ++i) + { + // ARGB 0xAARRGGBB -> RGBA 0xRRGGBBAA + EXPECT_EQ ((rgba[i] >> 24) & 0xffu, (uint32_t) ((i * 3) & 0xff)); // old R -> new R + EXPECT_EQ ((rgba[i] >> 16) & 0xffu, (uint32_t) ((i * 5) & 0xff)); // old G -> new G + EXPECT_EQ ((rgba[i] >> 8) & 0xffu, (uint32_t) (i & 0xff)); // old B -> new B + EXPECT_EQ (rgba[i] & 0xffu, (uint32_t) ((i * 7) & 0xff)); // old A -> new A + } +} + +TEST (ColorVectorOpsTests, ConvertARGBtoRGBAOddPixelCount) +{ + // 5 pixels — not a multiple of 4, exercises both SIMD and scalar tail. + const uint32_t argb[] = { + 0x12345678u, 0xaabbccddu, 0xffeeddccu, 0x01020304u, 0x99887766u + }; + uint32_t rgba[5] = {}; + + ColorVectorOperations::convertARGBtoRGBA (argb, rgba, 5); + + EXPECT_EQ (rgba[0], 0x34567812u); + EXPECT_EQ (rgba[1], 0xbbccddaau); + EXPECT_EQ (rgba[2], 0xeeddccffu); + EXPECT_EQ (rgba[3], 0x02030401u); + EXPECT_EQ (rgba[4], 0x88776699u); +} + // ============================================================================== // convertGrayscaleToRGBA tests // ============================================================================== TEST (ColorVectorOpsTests, ConvertGrayscaleToRGBA) { - const uint8 gray[] = { 0, 127, 255 }; - uint8 rgba[12] = {}; + const uint8_t gray[] = { 0, 127, 255 }; + uint32_t rgba[3] = {}; ColorVectorOperations::convertGrayscaleToRGBA (gray, rgba, 3); - EXPECT_EQ (rgba[0], 0); - EXPECT_EQ (rgba[1], 0); - EXPECT_EQ (rgba[2], 0); - EXPECT_EQ (rgba[3], 255); - EXPECT_EQ (rgba[4], 127); - EXPECT_EQ (rgba[5], 127); - EXPECT_EQ (rgba[6], 127); - EXPECT_EQ (rgba[7], 255); - EXPECT_EQ (rgba[8], 255); - EXPECT_EQ (rgba[9], 255); - EXPECT_EQ (rgba[10], 255); - EXPECT_EQ (rgba[11], 255); + EXPECT_EQ (rgba[0], makeRGBA (0, 0, 0, 255)); + EXPECT_EQ (rgba[1], makeRGBA (127, 127, 127, 255)); + EXPECT_EQ (rgba[2], makeRGBA (255, 255, 255, 255)); } TEST (ColorVectorOpsTests, ConvertGrayscaleToRGBAAlwaysOpaqueAlpha) { - const uint8 gray[] = { 128 }; - uint8 rgba[4] = {}; + const uint8_t gray[] = { 128 }; + uint32_t rgba[1] = {}; ColorVectorOperations::convertGrayscaleToRGBA (gray, rgba, 1); - EXPECT_EQ (rgba[0], 128); - EXPECT_EQ (rgba[1], 128); - EXPECT_EQ (rgba[2], 128); - EXPECT_EQ (rgba[3], 255); + EXPECT_EQ (rgba[0], makeRGBA (128, 128, 128, 255)); } TEST (ColorVectorOpsTests, ConvertGrayscaleToRGBAZeroPixels) { - const uint8 gray[] = { 99 }; - uint8 rgba[4] = { 1, 2, 3, 4 }; + const uint8_t gray[] = { 99 }; + uint32_t rgba[] = { 0x04030201u }; ColorVectorOperations::convertGrayscaleToRGBA (gray, rgba, 0); - EXPECT_EQ (rgba[0], 1); + EXPECT_EQ (rgba[0], 0x04030201u); } // ============================================================================== @@ -242,38 +397,29 @@ TEST (ColorVectorOpsTests, ConvertGrayscaleToRGBAZeroPixels) TEST (ColorVectorOpsTests, ConvertRGBToRGBA) { - const uint8 rgb[] = { 1, 2, 3, 4, 5, 6 }; - uint8 rgba[8] = {}; + const uint8_t rgb[] = { 1, 2, 3, 4, 5, 6 }; + uint32_t rgba[2] = {}; ColorVectorOperations::convertRGBToRGBA (rgb, rgba, 2); - EXPECT_EQ (rgba[0], 1); - EXPECT_EQ (rgba[1], 2); - EXPECT_EQ (rgba[2], 3); - EXPECT_EQ (rgba[3], 255); - EXPECT_EQ (rgba[4], 4); - EXPECT_EQ (rgba[5], 5); - EXPECT_EQ (rgba[6], 6); - EXPECT_EQ (rgba[7], 255); + EXPECT_EQ (rgba[0], makeRGBA (1, 2, 3, 255)); + EXPECT_EQ (rgba[1], makeRGBA (4, 5, 6, 255)); } TEST (ColorVectorOpsTests, ConvertRGBToRGBAAlwaysOpaqueAlpha) { - const uint8 rgb[] = { 10, 20, 30 }; - uint8 rgba[4] = {}; + const uint8_t rgb[] = { 10, 20, 30 }; + uint32_t rgba[1] = {}; ColorVectorOperations::convertRGBToRGBA (rgb, rgba, 1); - EXPECT_EQ (rgba[0], 10); - EXPECT_EQ (rgba[1], 20); - EXPECT_EQ (rgba[2], 30); - EXPECT_EQ (rgba[3], 255); + EXPECT_EQ (rgba[0], makeRGBA (10, 20, 30, 255)); } TEST (ColorVectorOpsTests, ConvertRGBToRGBAZeroPixels) { - const uint8 rgb[] = { 99, 98, 97 }; - uint8 rgba[4] = { 1, 2, 3, 4 }; + const uint8_t rgb[] = { 99, 98, 97 }; + uint32_t rgba[] = { 0x04030201u }; ColorVectorOperations::convertRGBToRGBA (rgb, rgba, 0); - EXPECT_EQ (rgba[0], 1); + EXPECT_EQ (rgba[0], 0x04030201u); } // ============================================================================== @@ -355,3 +501,140 @@ TEST (ColorVectorOpsTests, LerpRowsMultiplePixels) for (int i = 0; i < 16; ++i) EXPECT_NEAR (dst[i], rowA[i] + (rowB[i] - rowA[i]) * 0.5f, 1.0e-4f); } + +// ============================================================================== +// convertBGRAtoRGBA tests +// ============================================================================== + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBASwapsRedAndBlue) +{ + // BGRA pixels: bytes [B,G,R,A], swap → RGBA: bytes [R,G,B,A] + uint32_t pixels[] = { + makeBGRA (10, 20, 30, 40), + makeBGRA (50, 60, 70, 80) + }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 2); + + EXPECT_EQ (pixels[0], makeBGRA (30, 20, 10, 40)); // R↔B + EXPECT_EQ (pixels[1], makeBGRA (70, 60, 50, 80)); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBASinglePixel) +{ + uint32_t pixels[] = { makeBGRA (1, 2, 3, 255) }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 1); + + EXPECT_EQ (pixels[0], makeBGRA (3, 2, 1, 255)); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBAAllChannelsEqual) +{ + // When R == B, the swap is a no-op. + uint32_t pixels[] = { makeBGRA (100, 200, 100, 255) }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 1); + + EXPECT_EQ (pixels[0], makeBGRA (100, 200, 100, 255)); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBAAllZero) +{ + uint32_t pixels[] = { 0x00000000u, 0x00000000u }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 2); + + EXPECT_EQ (pixels[0], 0x00000000u); + EXPECT_EQ (pixels[1], 0x00000000u); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBAAllOnes) +{ + uint32_t pixels[] = { 0xffffffffu, 0xffffffffu }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 2); + + EXPECT_EQ (pixels[0], 0xffffffffu); + EXPECT_EQ (pixels[1], 0xffffffffu); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBAZeroPixels) +{ + uint32_t pixels[] = { makeBGRA (99, 98, 97, 96) }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 0); + + EXPECT_EQ (pixels[0], makeBGRA (99, 98, 97, 96)); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBANegativeCount) +{ + uint32_t pixels[] = { makeBGRA (1, 2, 3, 4) }; + + // Should no-op gracefully. + ColorVectorOperations::convertBGRAtoRGBA (pixels, -1); + + EXPECT_EQ (pixels[0], makeBGRA (1, 2, 3, 4)); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBALargePixelCount) +{ + // Exercise SIMD path with 100 pixels — well past any scalar tail boundary. + constexpr int kCount = 100; + uint32_t pixels[kCount]; + for (int i = 0; i < kCount; ++i) + pixels[i] = makeBGRA ((uint8_t) (i & 0xff), + (uint8_t) ((i * 2) & 0xff), + (uint8_t) ((i * 3) & 0xff), + (uint8_t) ((i * 5) & 0xff)); + + ColorVectorOperations::convertBGRAtoRGBA (pixels, kCount); + + for (int i = 0; i < kCount; ++i) + { + const auto p = pixels[i]; + EXPECT_EQ ((p >> 0) & 0xffu, (uint32_t) ((i * 3) & 0xff)); // was B + EXPECT_EQ ((p >> 8) & 0xffu, (uint32_t) ((i * 2) & 0xff)); // G unchanged + EXPECT_EQ ((p >> 16) & 0xffu, (uint32_t) (i & 0xff)); // was R + EXPECT_EQ ((p >> 24) & 0xffu, (uint32_t) ((i * 5) & 0xff)); // A unchanged + } +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGBAOddPixelCount) +{ + // 5 pixels — not a multiple of 4 (SIMD) pixels. + // The scalar tail path handles the last pixel. + uint32_t pixels[] = { + makeBGRA (1, 2, 10, 255), + makeBGRA (3, 4, 20, 128), + makeBGRA (5, 6, 30, 64), + makeBGRA (7, 8, 40, 32), + makeBGRA (9, 11, 50, 16) + }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 5); + + EXPECT_EQ (pixels[0], makeBGRA (10, 2, 1, 255)); + EXPECT_EQ (pixels[1], makeBGRA (20, 4, 3, 128)); + EXPECT_EQ (pixels[2], makeBGRA (30, 6, 5, 64)); + EXPECT_EQ (pixels[3], makeBGRA (40, 8, 7, 32)); + EXPECT_EQ (pixels[4], makeBGRA (50, 11, 9, 16)); +} + +TEST (ColorVectorOpsTests, ConvertBGRAtoRGRADoubleSwapIsIdentity) +{ + // Two swaps should restore the original values. + uint32_t pixels[] = { + makeBGRA (10, 20, 30, 40), + makeBGRA (50, 60, 70, 80), + makeBGRA (90, 100, 110, 120) + }; + + ColorVectorOperations::convertBGRAtoRGBA (pixels, 3); + ColorVectorOperations::convertBGRAtoRGBA (pixels, 3); + + EXPECT_EQ (pixels[0], makeBGRA (10, 20, 30, 40)); + EXPECT_EQ (pixels[1], makeBGRA (50, 60, 70, 80)); + EXPECT_EQ (pixels[2], makeBGRA (90, 100, 110, 120)); +} diff --git a/tests/yup_simd/yup_SIMDRegister.cpp b/tests/yup_simd/yup_SIMDRegister.cpp index 43ea61de3..1cd0f2252 100644 --- a/tests/yup_simd/yup_SIMDRegister.cpp +++ b/tests/yup_simd/yup_SIMDRegister.cpp @@ -26,12 +26,12 @@ using namespace yup; // ============================================================================== -// Float4 tests +// Float32x4 tests // ============================================================================== TEST (SIMDRegisterTests, Float4DefaultConstructorIsZero) { - Float4 r; + Float32x4 r; float stored[4] = { 1.0f, 2.0f, 3.0f, 4.0f }; r.storeUnaligned (stored); @@ -41,7 +41,7 @@ TEST (SIMDRegisterTests, Float4DefaultConstructorIsZero) TEST (SIMDRegisterTests, Float4ZeroHelperIsAllZero) { - const auto r = Float4::zero(); + const auto r = Float32x4::zero(); float stored[4] = {}; r.storeUnaligned (stored); @@ -51,7 +51,7 @@ TEST (SIMDRegisterTests, Float4ZeroHelperIsAllZero) TEST (SIMDRegisterTests, Float4BroadcastFillsAllLanes) { - const auto r = Float4::broadcast (3.14f); + const auto r = Float32x4::broadcast (3.14f); float stored[4] = {}; r.storeUnaligned (stored); @@ -62,7 +62,7 @@ TEST (SIMDRegisterTests, Float4BroadcastFillsAllLanes) TEST (SIMDRegisterTests, Float4ElementAccessOperator) { const float values[4] = { 10.0f, 20.0f, 30.0f, 40.0f }; - const auto r = Float4::loadUnaligned (values); + const auto r = Float32x4::loadUnaligned (values); EXPECT_FLOAT_EQ (r[0], 10.0f); EXPECT_FLOAT_EQ (r[1], 20.0f); @@ -75,9 +75,9 @@ TEST (SIMDRegisterTests, Float4ArithmeticAndHorizontalOps) const float aValues[4] = { 1.0f, -2.0f, 3.0f, -4.0f }; const float bValues[4] = { 5.0f, 6.0f, -7.0f, -8.0f }; - const auto a = Float4::loadUnaligned (aValues); - const auto b = Float4::loadUnaligned (bValues); - const auto result = a + b * Float4::broadcast (2.0f); + const auto a = Float32x4::loadUnaligned (aValues); + const auto b = Float32x4::loadUnaligned (bValues); + const auto result = a + b * Float32x4::broadcast (2.0f); float stored[4] = {}; result.storeUnaligned (stored); @@ -94,8 +94,8 @@ TEST (SIMDRegisterTests, Float4Subtraction) const float aValues[4] = { 10.0f, 20.0f, 30.0f, 40.0f }; const float bValues[4] = { 1.0f, 3.0f, 5.0f, 7.0f }; - const auto a = Float4::loadUnaligned (aValues); - const auto b = Float4::loadUnaligned (bValues); + const auto a = Float32x4::loadUnaligned (aValues); + const auto b = Float32x4::loadUnaligned (bValues); const auto result = a - b; float stored[4] = {}; @@ -110,8 +110,8 @@ TEST (SIMDRegisterTests, Float4Division) const float aValues[4] = { 4.0f, 9.0f, 16.0f, 25.0f }; const float bValues[4] = { 2.0f, 3.0f, 4.0f, 5.0f }; - const auto a = Float4::loadUnaligned (aValues); - const auto b = Float4::loadUnaligned (bValues); + const auto a = Float32x4::loadUnaligned (aValues); + const auto b = Float32x4::loadUnaligned (bValues); const auto result = a / b; float stored[4] = {}; @@ -126,8 +126,8 @@ TEST (SIMDRegisterTests, Float4CompoundAddAssign) const float aValues[4] = { 1.0f, 2.0f, 3.0f, 4.0f }; const float bValues[4] = { 10.0f, 20.0f, 30.0f, 40.0f }; - auto a = Float4::loadUnaligned (aValues); - const auto b = Float4::loadUnaligned (bValues); + auto a = Float32x4::loadUnaligned (aValues); + const auto b = Float32x4::loadUnaligned (bValues); a += b; float stored[4] = {}; @@ -142,8 +142,8 @@ TEST (SIMDRegisterTests, Float4CompoundMulAssign) const float aValues[4] = { 1.0f, 2.0f, 3.0f, 4.0f }; const float bValues[4] = { 2.0f, 3.0f, 4.0f, 5.0f }; - auto a = Float4::loadUnaligned (aValues); - const auto b = Float4::loadUnaligned (bValues); + auto a = Float32x4::loadUnaligned (aValues); + const auto b = Float32x4::loadUnaligned (bValues); a *= b; float stored[4] = {}; @@ -158,8 +158,8 @@ TEST (SIMDRegisterTests, Float4ElementwiseMin) const float aValues[4] = { 1.0f, 5.0f, 2.0f, 4.0f }; const float bValues[4] = { 3.0f, 2.0f, 4.0f, 1.0f }; - const auto a = Float4::loadUnaligned (aValues); - const auto b = Float4::loadUnaligned (bValues); + const auto a = Float32x4::loadUnaligned (aValues); + const auto b = Float32x4::loadUnaligned (bValues); const auto result = a.min (b); float stored[4] = {}; @@ -174,8 +174,8 @@ TEST (SIMDRegisterTests, Float4ElementwiseMax) const float aValues[4] = { 1.0f, 5.0f, 2.0f, 4.0f }; const float bValues[4] = { 3.0f, 2.0f, 4.0f, 1.0f }; - const auto a = Float4::loadUnaligned (aValues); - const auto b = Float4::loadUnaligned (bValues); + const auto a = Float32x4::loadUnaligned (aValues); + const auto b = Float32x4::loadUnaligned (bValues); const auto result = a.max (b); float stored[4] = {}; @@ -188,7 +188,7 @@ TEST (SIMDRegisterTests, Float4ElementwiseMax) TEST (SIMDRegisterTests, Float4AbsOnMixedValues) { const float values[4] = { -1.0f, 2.0f, -3.0f, 4.0f }; - const auto r = Float4::loadUnaligned (values); + const auto r = Float32x4::loadUnaligned (values); const auto result = r.abs(); float stored[4] = {}; @@ -203,21 +203,21 @@ TEST (SIMDRegisterTests, Float4AbsOnMixedValues) TEST (SIMDRegisterTests, Float4SumAllLanes) { const float values[4] = { 1.0f, 2.0f, 3.0f, 4.0f }; - const auto r = Float4::loadUnaligned (values); + const auto r = Float32x4::loadUnaligned (values); EXPECT_FLOAT_EQ (r.sum(), 10.0f); } TEST (SIMDRegisterTests, Float4SumWithNegatives) { const float values[4] = { 1.0f, -1.0f, 2.0f, -2.0f }; - const auto r = Float4::loadUnaligned (values); + const auto r = Float32x4::loadUnaligned (values); EXPECT_FLOAT_EQ (r.sum(), 0.0f); } TEST (SIMDRegisterTests, Float4HmaxFindsLargest) { const float values[4] = { -3.0f, 7.0f, 1.0f, -10.0f }; - const auto r = Float4::loadUnaligned (values); + const auto r = Float32x4::loadUnaligned (values); EXPECT_FLOAT_EQ (r.hmax(), 7.0f); } @@ -228,7 +228,7 @@ TEST (SIMDRegisterTests, MulAddAndLoadStoreRoundTrip) alignas (16) const float add[4] = { 10.0f, 20.0f, 30.0f, 40.0f }; alignas (16) float stored[4] = {}; - const auto result = Float4::loadAligned (base).mulAdd (Float4::loadAligned (mul), Float4::loadAligned (add)); + const auto result = Float32x4::loadAligned (base).mulAdd (Float32x4::loadAligned (mul), Float32x4::loadAligned (add)); result.storeAligned (stored); for (int i = 0; i < 4; ++i) @@ -238,7 +238,7 @@ TEST (SIMDRegisterTests, MulAddAndLoadStoreRoundTrip) TEST (SIMDRegisterTests, Float4LoadFromPointerConstructor) { const float values[4] = { 5.0f, 6.0f, 7.0f, 8.0f }; - const Float4 r (values); + const Float32x4 r (values); float stored[4] = {}; r.storeUnaligned (stored); @@ -249,26 +249,26 @@ TEST (SIMDRegisterTests, Float4LoadFromPointerConstructor) TEST (SIMDRegisterTests, Float4ScalarConstructorBroadcasts) { - const Float4 r (42.0f); + const Float32x4 r (42.0f); for (int i = 0; i < 4; ++i) EXPECT_FLOAT_EQ (r[i], 42.0f); } // ============================================================================== -// Float8 tests +// Float32x8 tests // ============================================================================== TEST (SIMDRegisterTests, Float8DefaultConstructorIsZero) { - Float8 r; + Float32x8 r; for (int i = 0; i < 8; ++i) EXPECT_FLOAT_EQ (r[i], 0.0f); } TEST (SIMDRegisterTests, Float8BroadcastAndArithmetic) { - const Float8 a = Float8::broadcast (2.0f); - const Float8 b = Float8::broadcast (3.0f); + const Float32x8 a = Float32x8::broadcast (2.0f); + const Float32x8 b = Float32x8::broadcast (3.0f); const auto result = a * b; for (int i = 0; i < 8; ++i) @@ -278,7 +278,7 @@ TEST (SIMDRegisterTests, Float8BroadcastAndArithmetic) TEST (SIMDRegisterTests, Float8LoadStoreRoundTrip) { float values[8] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f }; - const auto r = Float8::loadUnaligned (values); + const auto r = Float32x8::loadUnaligned (values); float stored[8] = {}; r.storeUnaligned (stored); @@ -290,14 +290,14 @@ TEST (SIMDRegisterTests, Float8LoadStoreRoundTrip) TEST (SIMDRegisterTests, Float8Sum) { float values[8] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f }; - const auto r = Float8::loadUnaligned (values); + const auto r = Float32x8::loadUnaligned (values); EXPECT_FLOAT_EQ (r.sum(), 36.0f); } TEST (SIMDRegisterTests, Float8Hmax) { float values[8] = { 1.0f, -5.0f, 3.0f, 9.0f, 2.0f, -3.0f, 4.0f, 0.0f }; - const auto r = Float8::loadUnaligned (values); + const auto r = Float32x8::loadUnaligned (values); EXPECT_FLOAT_EQ (r.hmax(), 9.0f); } @@ -307,18 +307,18 @@ TEST (SIMDRegisterTests, Float8AbsNegatesAll) for (int i = 0; i < 8; ++i) values[i] = (i % 2 == 0) ? -(float) (i + 1) : (float) (i + 1); - const auto r = Float8::loadUnaligned (values).abs(); + const auto r = Float32x8::loadUnaligned (values).abs(); for (int i = 0; i < 8; ++i) EXPECT_FLOAT_EQ (r[i], (float) (i + 1)); } // ============================================================================== -// Double2 tests +// Float64x2 tests // ============================================================================== TEST (SIMDRegisterTests, Double2DefaultConstructorIsZero) { - Double2 r; + Float64x2 r; for (int i = 0; i < 2; ++i) EXPECT_DOUBLE_EQ (r[i], 0.0); } @@ -328,8 +328,8 @@ TEST (SIMDRegisterTests, Double2ArithmeticOperations) const double aValues[2] = { 1.5, -2.5 }; const double bValues[2] = { 3.0, 4.0 }; - const auto a = Double2::loadUnaligned (aValues); - const auto b = Double2::loadUnaligned (bValues); + const auto a = Float64x2::loadUnaligned (aValues); + const auto b = Float64x2::loadUnaligned (bValues); const auto sum = a + b; const auto diff = a - b; @@ -348,7 +348,7 @@ TEST (SIMDRegisterTests, Double2ArithmeticOperations) TEST (SIMDRegisterTests, Double2BroadcastAndSum) { - const auto r = Double2::broadcast (3.14); + const auto r = Float64x2::broadcast (3.14); EXPECT_NEAR (r.sum(), 6.28, 1.0e-12); } @@ -357,8 +357,8 @@ TEST (SIMDRegisterTests, Double2MinMax) const double aValues[2] = { 1.0, 5.0 }; const double bValues[2] = { 3.0, 2.0 }; - const auto a = Double2::loadUnaligned (aValues); - const auto b = Double2::loadUnaligned (bValues); + const auto a = Float64x2::loadUnaligned (aValues); + const auto b = Float64x2::loadUnaligned (bValues); const auto minResult = a.min (b); const auto maxResult = a.max (b); @@ -370,13 +370,13 @@ TEST (SIMDRegisterTests, Double2MinMax) } // ============================================================================== -// Double4 tests +// Float64x4 tests // ============================================================================== TEST (SIMDRegisterTests, Double4LoadStoreRoundTrip) { const double values[4] = { 1.1, 2.2, 3.3, 4.4 }; - const auto r = Double4::loadUnaligned (values); + const auto r = Float64x4::loadUnaligned (values); double stored[4] = {}; r.storeUnaligned (stored); @@ -391,9 +391,9 @@ TEST (SIMDRegisterTests, Double4MulAdd) const double mulValues[4] = { 2.0, 3.0, 4.0, 5.0 }; const double addValues[4] = { 10.0, 20.0, 30.0, 40.0 }; - const auto base = Double4::loadUnaligned (baseValues); - const auto mul = Double4::loadUnaligned (mulValues); - const auto add = Double4::loadUnaligned (addValues); + const auto base = Float64x4::loadUnaligned (baseValues); + const auto mul = Float64x4::loadUnaligned (mulValues); + const auto add = Float64x4::loadUnaligned (addValues); const auto result = base.mulAdd (mul, add); for (int i = 0; i < 4; ++i) @@ -403,14 +403,14 @@ TEST (SIMDRegisterTests, Double4MulAdd) TEST (SIMDRegisterTests, Double4Sum) { const double values[4] = { 1.0, 2.0, 3.0, 4.0 }; - const auto r = Double4::loadUnaligned (values); + const auto r = Float64x4::loadUnaligned (values); EXPECT_DOUBLE_EQ (r.sum(), 10.0); } TEST (SIMDRegisterTests, Double4Hmax) { const double values[4] = { -1.0, 3.5, 2.0, -4.0 }; - const auto r = Double4::loadUnaligned (values); + const auto r = Float64x4::loadUnaligned (values); EXPECT_DOUBLE_EQ (r.hmax(), 3.5); } diff --git a/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.hpp b/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.hpp index ad1486034..63b9ec63b 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.hpp +++ b/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.glsl.hpp @@ -168,7 +168,7 @@ l0=min(l0,k.Ee); #endif return l0;} #ifdef O -e void yb(uint V0,o4 O0,T4(d)n){ +e void yb(uint V0,o4 O0,T4(d) n){ #ifdef OC if(all(lessThan(abs(O0.xy-unpackUnorm4x8(V0).xy),A2(.25/255.))))n=min(n,O0.z);else n=.0; #else @@ -176,9 +176,9 @@ if(V0==O0>>16)n=min(n,unpackHalf2x16(O0).x);else n=.0; #endif } #endif -e void Y7(uint l0,d m0,e1(i)P +e void Y7(uint l0,d m0,e1(i) P #if defined(O)&&!defined(IC) -,T4(o4)q1 +,T4(o4) q1 #endif G6 P3){W0 r1=L5(TC,l0);d n=m0;if((r1.x&(Fe|C9))!=0u){n=abs(n); #ifdef PC diff --git a/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.minified.glsl b/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.minified.glsl index 8e0f2b95d..ffb36c3a7 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.minified.glsl +++ b/thirdparty/rive_renderer/source/generated/shaders/atomic_draw.minified.glsl @@ -161,7 +161,7 @@ l0=min(l0,k.Ee); #endif return l0;} #ifdef ENABLE_CLIPPING -e void yb(uint V0,o4 O0,T4(d)n){ +e void yb(uint V0,o4 O0,T4(d) n){ #ifdef PLS_BLEND_SRC_OVER if(all(lessThan(abs(O0.xy-unpackUnorm4x8(V0).xy),A2(.25/255.))))n=min(n,O0.z);else n=.0; #else @@ -169,9 +169,9 @@ if(V0==O0>>16)n=min(n,unpackHalf2x16(O0).x);else n=.0; #endif } #endif -e void Y7(uint l0,d m0,e1(i)P +e void Y7(uint l0,d m0,e1(i) P #if defined(ENABLE_CLIPPING)&&!defined(RESOLVE_PLS) -,T4(o4)q1 +,T4(o4) q1 #endif G6 P3){W0 r1=L5(TC,l0);d n=m0;if((r1.x&(Fe|C9))!=0u){n=abs(n); #ifdef ENABLE_EVEN_ODD diff --git a/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.hpp b/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.hpp index 74400cc47..d2d7a90f1 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.hpp +++ b/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.glsl.hpp @@ -11,7 +11,7 @@ const char bezier_utils[] = R"===(#ifndef Hb #ifndef J6 #define J6 c #endif -e float K9(c o,c b){float Le=dot(o,b);float Ib=dot(o,o)*dot(b,b);return(Ib==.0)?1.:clamp(Le*inversesqrt(Ib),-1.,1.);}e void Me(c w0,c x0,c E0,c I0,e1(c)A,e1(c)F,e1(c)d2){d2=x0-w0;c K6=E0-x0;c h8=I0-w0;F=K6-d2;A=-3.*K6+h8;}e Z L9(c w0,c x0,c E0,c I0){Z t;t[0]=(any(notEqual(w0,x0))?x0:any(notEqual(x0,E0))?E0:I0)-w0;t[1]=I0-(any(notEqual(I0,E0))?E0:any(notEqual(E0,x0))?x0:w0);return t;}e float Ne(c w0,c x0,c E0,c I0,float w1,float Oe){c A,F,d2;Me(w0,x0,E0,I0,A,F,d2);c L6=3.*(((A*w1)+2.*F)*w1+d2);float Jb=length(L6);if(Jb==.0){return.0;}L6*=1./Jb;float i8=2.*dot(A,L6);float M6=3.*(i8*w1+4.*dot(F,L6))*w1+6.*dot(d2,L6);float M9=min(w1,1.-w1);float Pe=(i8*M9*M9+M6)*M9;float Kb=min(Oe,Pe*.9999);float V2;if(i8==.0){V2=Kb/M6;}else{float H=1./i8;float b=M6*H,I1=-Kb*H;float N6=(-1./3.)*b,O6=.5*I1;float Lb=O6*O6-N6*N6*N6;if(Lb<.0){float j8=sqrt(N6);float h1=acos(O6/(j8*j8*j8));V2=-2.*j8*cos(h1*(1./3.)+(-A3*2./3.));}else{float A=pow(abs(O6)+sqrt(Lb),1./3.);if(O6<.0)A=-A;V2=A!=.0?A+N6/A:.0;}}V2=abs(V2);g t0011=w1+Hb(-V2,-V2,V2,V2);g Mb=(A.xyxy*t0011+2.*F.xyxy)*t0011+d2.xyxy;Z F2=L9(w0,x0,E0,I0);c Qe=t0011.x<1e-3?F2[0]:Mb.xy;c Re=t0011.z>1.-1e-3?F2[1]:Mb.zw;return acos(K9(Qe,Re));}e float k8(float o,float b){o=b<.0?-o:o;b=abs(b);return o>.0?(o1.-1e-3?F2[1]:Mb.zw;return acos(K9(Qe,Re));}e float k8(float o,float b){o=b<.0?-o:o;b=abs(b);return o>.0?(oT5.y?P6.x:P6.y;return max(T5.x,T5.y); #else diff --git a/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.minified.glsl b/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.minified.glsl index dac5f0032..cc951d9f9 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.minified.glsl +++ b/thirdparty/rive_renderer/source/generated/shaders/bezier_utils.minified.glsl @@ -4,7 +4,7 @@ #ifndef J6 #define J6 c #endif -e float K9(c o,c b){float Le=dot(o,b);float Ib=dot(o,o)*dot(b,b);return(Ib==.0)?1.:clamp(Le*inversesqrt(Ib),-1.,1.);}e void Me(c w0,c x0,c E0,c I0,e1(c)A,e1(c)F,e1(c)d2){d2=x0-w0;c K6=E0-x0;c h8=I0-w0;F=K6-d2;A=-3.*K6+h8;}e Z L9(c w0,c x0,c E0,c I0){Z t;t[0]=(any(notEqual(w0,x0))?x0:any(notEqual(x0,E0))?E0:I0)-w0;t[1]=I0-(any(notEqual(I0,E0))?E0:any(notEqual(E0,x0))?x0:w0);return t;}e float Ne(c w0,c x0,c E0,c I0,float w1,float Oe){c A,F,d2;Me(w0,x0,E0,I0,A,F,d2);c L6=3.*(((A*w1)+2.*F)*w1+d2);float Jb=length(L6);if(Jb==.0){return.0;}L6*=1./Jb;float i8=2.*dot(A,L6);float M6=3.*(i8*w1+4.*dot(F,L6))*w1+6.*dot(d2,L6);float M9=min(w1,1.-w1);float Pe=(i8*M9*M9+M6)*M9;float Kb=min(Oe,Pe*.9999);float V2;if(i8==.0){V2=Kb/M6;}else{float H=1./i8;float b=M6*H,I1=-Kb*H;float N6=(-1./3.)*b,O6=.5*I1;float Lb=O6*O6-N6*N6*N6;if(Lb<.0){float j8=sqrt(N6);float h1=acos(O6/(j8*j8*j8));V2=-2.*j8*cos(h1*(1./3.)+(-A3*2./3.));}else{float A=pow(abs(O6)+sqrt(Lb),1./3.);if(O6<.0)A=-A;V2=A!=.0?A+N6/A:.0;}}V2=abs(V2);g t0011=w1+Hb(-V2,-V2,V2,V2);g Mb=(A.xyxy*t0011+2.*F.xyxy)*t0011+d2.xyxy;Z F2=L9(w0,x0,E0,I0);c Qe=t0011.x<1e-3?F2[0]:Mb.xy;c Re=t0011.z>1.-1e-3?F2[1]:Mb.zw;return acos(K9(Qe,Re));}e float k8(float o,float b){o=b<.0?-o:o;b=abs(b);return o>.0?(o1.-1e-3?F2[1]:Mb.zw;return acos(K9(Qe,Re));}e float k8(float o,float b){o=b<.0?-o:o;b=abs(b);return o>.0?(oT5.y?P6.x:P6.y;return max(T5.x,T5.y); #else diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.hpp b/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.hpp index 13f55a488..daabdbe25 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.hpp +++ b/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.frag.hpp @@ -14,7 +14,7 @@ r0(R2,d0); #ifndef K Ja(d6,z6); #endif -K1 N3 Ea(ga,Yd,S0);O3 e void xh(T4(float)o3,d o0,uint T1,e1(uint)p1,e1(d)J3){ +K1 N3 Ea(ga,Yd,S0);O3 e void xh(T4(float) o3,d o0,uint T1,e1(uint) p1,e1(d) J3){ #ifdef K if(min(o3,o0)>=1.){return;} #endif @@ -30,7 +30,7 @@ d V1=V5(p1&ja)*ha;d G1=max(V1,o0);q=K8(V1,G1,o3); #ifndef K J3=G1; #endif -}o3*=q;}e void yh(T4(float)o3,d P4,uint T1,e1(uint)p1,e1(d)J3){d q=.0;uint fb=q7(abs(P4));p1=pd(S0,T1); +}o3*=q;}e void yh(T4(float) o3,d P4,uint T1,e1(uint) p1,e1(d) J3){d q=.0;uint fb=q7(abs(P4));p1=pd(S0,T1); #ifdef K if(min(o3,P4)>=1.&&(p1=(k.W1|j5))){return;} #endif diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.minified.frag b/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.minified.frag index 4fd50e2e0..27d51c1ac 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.minified.frag +++ b/thirdparty/rive_renderer/source/generated/shaders/draw_clockwise_atomic_path.minified.frag @@ -7,7 +7,7 @@ r0(R2,d0); #ifndef FIXED_FUNCTION_COLOR_OUTPUT Ja(d6,z6); #endif -K1 N3 Ea(ga,Yd,S0);O3 e void xh(T4(float)o3,d o0,uint T1,e1(uint)p1,e1(d)J3){ +K1 N3 Ea(ga,Yd,S0);O3 e void xh(T4(float) o3,d o0,uint T1,e1(uint) p1,e1(d) J3){ #ifdef FIXED_FUNCTION_COLOR_OUTPUT if(min(o3,o0)>=1.){return;} #endif @@ -23,7 +23,7 @@ d V1=V5(p1&ja)*ha;d G1=max(V1,o0);q=K8(V1,G1,o3); #ifndef FIXED_FUNCTION_COLOR_OUTPUT J3=G1; #endif -}o3*=q;}e void yh(T4(float)o3,d P4,uint T1,e1(uint)p1,e1(d)J3){d q=.0;uint fb=q7(abs(P4));p1=pd(S0,T1); +}o3*=q;}e void yh(T4(float) o3,d P4,uint T1,e1(uint) p1,e1(d) J3){d q=.0;uint fb=q7(abs(P4));p1=pd(S0,T1); #ifdef FIXED_FUNCTION_COLOR_OUTPUT if(min(o3,P4)>=1.&&(p1=(k.W1|j5))){return;} #endif diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.hpp b/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.hpp index e22ffa804..16d988ba5 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.hpp +++ b/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.glsl.hpp @@ -51,11 +51,11 @@ g Ic(float ma,c D8,float F1){c g6=(1.-D8*abs(F1))*.5;float Y3,l5;if(abs(ma-T6)<1 e d d8(g J F3){d Y3=J.z;d l5=max(J.w,.0);d h6=Y3>=.0?g5(l5):.0;if(abs(Y3)>pc);}e float Lc(Z Y0,c Xf){c e2=Z0(Y0,Xf);return(abs(e2.x)+abs(e2.y))*(1./dot(e2,e2));}e bool p9(g h7,g oa,int T,e1(uint)c3,e1(c)Yf +e U m5(int Kc){return U(Kc&((1<>pc);}e float Lc(Z Y0,c Xf){c e2=Z0(Y0,Xf);return(abs(e2.x)+abs(e2.y))*(1./dot(e2,e2));}e bool p9(g h7,g oa,int T,e1(uint) c3,e1(c) Yf #ifndef BB -,e1(g)N1 +,e1(g) N1 #else -,e1(X)i7 +,e1(X) i7 #endif i6){int E8=int(h7.x);float F1=h7.y;float pa=h7.z;int Mc=floatBitsToInt(h7.w)>>2;int j7=floatBitsToInt(h7.w)&3;int qa=min(E8,Mc-1);int G4=T*Mc+qa;C4 n5=v1(DC,m5(G4));uint e0=f5(n5.w);uint F8=max(e0&wc,1u);Q ra=P0(XC,F8-1u);c Nc=uintBitsToFloat(ra.xy);c3=ra.z&0xffffu;uint Oc=ra.w;Z Y0=j2(uintBitsToFloat(P0(MB,c3*4u)));Q H4=P0(MB,c3*4u+1u);c c2=uintBitsToFloat(H4.xy);float H2=uintBitsToFloat(H4.z);float I2=uintBitsToFloat(H4.w);uint Pc=e0&D3;if(Pc!=0u){E8=int(oa.x);F1=oa.y;pa=oa.z;}if(E8!=qa){int Qc=G4+E8-qa;C4 Rc=v1(DC,m5(Qc));if((f5(Rc.w)&(D3|0xffffu))!=(e0&(D3|0xffffu))){bool Zf=H2==.0||Nc.x!=.0;if(Zf){G4=int(Oc);n5=v1(DC,m5(G4));}}else{G4=Qc;n5=Rc;}e0=(f5(n5.w)&~D3)|Pc;}float h1; #ifdef HB @@ -94,11 +94,11 @@ N1.xy=mix(N1.xy,c(1.,-1.),bf(k.og!=0u)); return true;} #endif #if defined(CB)&&defined(DB) -e c tb(V j6,e1(uint)c3 +e c tb(V j6,e1(uint) c3 #ifdef BB -,e1(X)i7 +,e1(X) i7 #else -,e1(d)pg +,e1(d) pg #endif i6){c3=floatBitsToUint(j6.z)&0xffffu; #ifdef BB @@ -109,11 +109,11 @@ pg=U9(floatBitsToInt(j6.z)>>16); c k6=j6.xy;Z Y0=j2(uintBitsToFloat(P0(MB,c3*4u)));Q H4=P0(MB,c3*4u+1u);c c2=uintBitsToFloat(H4.xy);k6=Z0(Y0,k6)+c2;return k6;} #endif #if defined(CB)&&defined(EB) -e c sb(V j6,e1(uint)c3, +e c sb(V j6,e1(uint) c3, #ifdef BB -e1(X)i7, +e1(X) i7, #endif -e1(c)qg i6){c3=floatBitsToUint(j6.z)&0xffffu;Q J4=P0(MB,c3*4u+2u); +e1(c) qg i6){c3=floatBitsToUint(j6.z)&0xffffu;Q J4=P0(MB,c3*4u+2u); #ifdef BB i7=i2(J4.x); #endif diff --git a/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.minified.glsl b/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.minified.glsl index c797cfff0..84df112fd 100644 --- a/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.minified.glsl +++ b/thirdparty/rive_renderer/source/generated/shaders/draw_path_common.minified.glsl @@ -44,11 +44,11 @@ g Ic(float ma,c D8,float F1){c g6=(1.-D8*abs(F1))*.5;float Y3,l5;if(abs(ma-T6)<1 e d d8(g J F3){d Y3=J.z;d l5=max(J.w,.0);d h6=Y3>=.0?g5(l5):.0;if(abs(Y3)>pc);}e float Lc(Z Y0,c Xf){c e2=Z0(Y0,Xf);return(abs(e2.x)+abs(e2.y))*(1./dot(e2,e2));}e bool p9(g h7,g oa,int T,e1(uint)c3,e1(c)Yf +e U m5(int Kc){return U(Kc&((1<>pc);}e float Lc(Z Y0,c Xf){c e2=Z0(Y0,Xf);return(abs(e2.x)+abs(e2.y))*(1./dot(e2,e2));}e bool p9(g h7,g oa,int T,e1(uint) c3,e1(c) Yf #ifndef RENDER_MODE_MSAA -,e1(g)N1 +,e1(g) N1 #else -,e1(X)i7 +,e1(X) i7 #endif i6){int E8=int(h7.x);float F1=h7.y;float pa=h7.z;int Mc=floatBitsToInt(h7.w)>>2;int j7=floatBitsToInt(h7.w)&3;int qa=min(E8,Mc-1);int G4=T*Mc+qa;C4 n5=v1(DC,m5(G4));uint e0=f5(n5.w);uint F8=max(e0&wc,1u);Q ra=P0(XC,F8-1u);c Nc=uintBitsToFloat(ra.xy);c3=ra.z&0xffffu;uint Oc=ra.w;Z Y0=j2(uintBitsToFloat(P0(MB,c3*4u)));Q H4=P0(MB,c3*4u+1u);c c2=uintBitsToFloat(H4.xy);float H2=uintBitsToFloat(H4.z);float I2=uintBitsToFloat(H4.w);uint Pc=e0&D3;if(Pc!=0u){E8=int(oa.x);F1=oa.y;pa=oa.z;}if(E8!=qa){int Qc=G4+E8-qa;C4 Rc=v1(DC,m5(Qc));if((f5(Rc.w)&(D3|0xffffu))!=(e0&(D3|0xffffu))){bool Zf=H2==.0||Nc.x!=.0;if(Zf){G4=int(Oc);n5=v1(DC,m5(G4));}}else{G4=Qc;n5=Rc;}e0=(f5(n5.w)&~D3)|Pc;}float h1; #ifdef ENABLE_FEATHER @@ -87,11 +87,11 @@ N1.xy=mix(N1.xy,c(1.,-1.),bf(k.og!=0u)); return true;} #endif #if defined(VERTEX)&&defined(DRAW_INTERIOR_TRIANGLES) -e c tb(V j6,e1(uint)c3 +e c tb(V j6,e1(uint) c3 #ifdef RENDER_MODE_MSAA -,e1(X)i7 +,e1(X) i7 #else -,e1(d)pg +,e1(d) pg #endif i6){c3=floatBitsToUint(j6.z)&0xffffu; #ifdef RENDER_MODE_MSAA @@ -102,11 +102,11 @@ pg=U9(floatBitsToInt(j6.z)>>16); c k6=j6.xy;Z Y0=j2(uintBitsToFloat(P0(MB,c3*4u)));Q H4=P0(MB,c3*4u+1u);c c2=uintBitsToFloat(H4.xy);k6=Z0(Y0,k6)+c2;return k6;} #endif #if defined(VERTEX)&&defined(ATLAS_BLIT) -e c sb(V j6,e1(uint)c3, +e c sb(V j6,e1(uint) c3, #ifdef RENDER_MODE_MSAA -e1(X)i7, +e1(X) i7, #endif -e1(c)qg i6){c3=floatBitsToUint(j6.z)&0xffffu;Q J4=P0(MB,c3*4u+2u); +e1(c) qg i6){c3=floatBitsToUint(j6.z)&0xffffu;Q J4=P0(MB,c3*4u+2u); #ifdef RENDER_MODE_MSAA i7=i2(J4.x); #endif diff --git a/thirdparty/rive_renderer/source/shaders/minify.py b/thirdparty/rive_renderer/source/shaders/minify.py index c01fade10..3395e0c96 100644 --- a/thirdparty/rive_renderer/source/shaders/minify.py +++ b/thirdparty/rive_renderer/source/shaders/minify.py @@ -481,6 +481,12 @@ def emit_tokens_to_rewritten_glsl(self, out, *, preserve_exported_switches, call out.write('\n') elif needs_whitespace and lasttoken_needs_whitespace: out.write(' ') + elif tok.type == "ID" and lasttoken.type == "OP" and lasttoken.value == ")": + # Mesa's GLSL compiler can reject minified GLSL when a ')' from a + # macro argument list is directly adjacent to the next identifier + # without any whitespace, e.g., OUT(float2)foo. + # Insert a space to work around this Mesa compiler bug. + out.write(' ') # is_newline will be false once we output the token (unless this value otherwise gets # updated).