add Comments

This commit is contained in:
2026-07-13 10:19:30 +08:00
parent 3c9592da45
commit e77de49406
12 changed files with 444 additions and 16 deletions
+42
View File
@@ -9,12 +9,16 @@
namespace olive
{
// Stores the requested backend name; the actual backend may later become
// OpenGL if loading or availability checks require a Vulkan fallback.
DynamicRenderer::DynamicRenderer(const QString &backend, QObject *parent)
: Renderer(parent)
, backend_(backend.toLower())
{
}
// Tears down the backend in the reverse order used by Load(): release renderer
// resources, destroy the opaque backend object, then unload the shared library.
DynamicRenderer::~DynamicRenderer()
{
Destroy();
@@ -28,6 +32,9 @@ DynamicRenderer::~DynamicRenderer()
}
}
// Builds the private backend library path for the current platform.
// The search is intentionally restricted to Oak-controlled directories so a
// system libGL/libvulkan loader is never mistaken for an Oak render backend.
QString DynamicRenderer::LibraryFilename() const
{
const QString base = backend_ == QStringLiteral("vulkan")
@@ -58,6 +65,9 @@ QString DynamicRenderer::LibraryFilename() const
return candidates.first();
}
// Loads the selected backend, resolves its C ABI table, creates the opaque
// backend object, and optionally falls back from Vulkan to OpenGL when runtime
// availability checks fail.
bool DynamicRenderer::Load()
{
if (handle_) {
@@ -106,6 +116,8 @@ bool DynamicRenderer::Load()
return handle_ != nullptr;
}
// Resolves the mandatory C ABI entry points from the loaded shared library.
// Optional information probes are resolved after the required render interface.
bool DynamicRenderer::ResolveFunctions()
{
ResetFunctions();
@@ -155,6 +167,8 @@ bool DynamicRenderer::ResolveFunctions()
return true;
}
// Discards a partially-created backend and restarts loading with the OpenGL
// backend. This keeps RenderManager's fallback path inside the adapter.
bool DynamicRenderer::FallbackToOpenGL()
{
if (handle_ && destroy_) {
@@ -169,6 +183,8 @@ bool DynamicRenderer::FallbackToOpenGL()
return Load();
}
// Clears all cached C function pointers so a failed backend cannot leave stale
// call targets behind for a later fallback load.
void DynamicRenderer::ResetFunctions()
{
create_ = nullptr;
@@ -195,16 +211,20 @@ void DynamicRenderer::ResetFunctions()
opengl_context_ = nullptr;
}
// Returns backend metadata exposed by the dynamic library when available.
bool DynamicRenderer::GetBackendInfo(OakRenderBackendInfo *out_info) const
{
return handle_ && get_info_ && out_info && get_info_(handle_, out_info);
}
// Initializes the loaded backend using its own context/device creation path.
bool DynamicRenderer::Init()
{
return Load() && init_(handle_);
}
// Initializes an OpenGL backend against an existing widget context; non-OpenGL
// backends may ignore the context on the library side.
bool DynamicRenderer::InitWithOpenGLContext(QOpenGLContext *context)
{
if (!Load()) {
@@ -214,6 +234,8 @@ bool DynamicRenderer::InitWithOpenGLContext(QOpenGLContext *context)
return true;
}
// Forwards post-destroy cleanup to the backend while the library is still
// loaded and its symbols are still valid.
void DynamicRenderer::PostDestroy()
{
if (handle_ && post_destroy_) {
@@ -221,6 +243,8 @@ void DynamicRenderer::PostDestroy()
}
}
// Runs backend post-initialization after Init/InitWithOpenGLContext has
// established the device or GL context.
void DynamicRenderer::PostInit()
{
if (handle_) {
@@ -228,12 +252,15 @@ void DynamicRenderer::PostInit()
}
}
// Forwards render target clearing through the C ABI.
void DynamicRenderer::ClearDestination(Texture *texture, double r, double g,
double b, double a)
{
clear_destination_(handle_, texture, r, g, b, a);
}
// Creates a backend-native shader and receives the result as an opaque QVariant
// because this first-generation ABI still shares C++/Qt types between modules.
QVariant DynamicRenderer::CreateNativeShader(ShaderCode code)
{
QVariant out;
@@ -241,11 +268,13 @@ QVariant DynamicRenderer::CreateNativeShader(ShaderCode code)
return out;
}
// Releases a backend-native shader handle.
void DynamicRenderer::DestroyNativeShader(QVariant shader)
{
destroy_native_shader_(handle_, &shader);
}
// Uploads CPU pixel data into a backend texture through the dynamic ABI.
void DynamicRenderer::UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize)
@@ -253,6 +282,7 @@ void DynamicRenderer::UploadToTexture(const QVariant &handle,
upload_to_texture_(handle_, &handle, &params, data, linesize);
}
// Downloads backend texture data into a caller-provided CPU buffer.
void DynamicRenderer::DownloadFromTexture(const QVariant &handle,
const VideoParams &params, void *data,
int linesize)
@@ -260,11 +290,13 @@ void DynamicRenderer::DownloadFromTexture(const QVariant &handle,
download_from_texture_(handle_, &handle, &params, data, linesize);
}
// Waits for backend work to become visible to subsequent CPU or GPU consumers.
void DynamicRenderer::Flush()
{
flush_(handle_);
}
// Reads a single pixel through the backend-provided readback hook.
Color DynamicRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
{
Color out;
@@ -272,6 +304,8 @@ Color DynamicRenderer::GetPixelFromTexture(Texture *texture, const QPointF &pt)
return out;
}
// Exposes the wrapped OpenGL context when the backend is OpenGL; Vulkan returns
// null so callers can avoid GL-only paths.
QOpenGLContext *DynamicRenderer::OpenGLContext() const
{
return opengl_context_ && handle_
@@ -279,11 +313,13 @@ QOpenGLContext *DynamicRenderer::OpenGLContext() const
: nullptr;
}
// Reports the effective backend after any load-time fallback has completed.
bool DynamicRenderer::IsOpenGL() const
{
return backend_ == QStringLiteral("opengl");
}
// Dispatches a shader blit to the loaded backend.
void DynamicRenderer::Blit(QVariant shader, AcceleratedJob &job,
Texture *destination, VideoParams destination_params,
bool clear_destination)
@@ -292,6 +328,7 @@ void DynamicRenderer::Blit(QVariant shader, AcceleratedJob &job,
clear_destination);
}
// Allocates a backend-native texture and wraps its opaque handle in QVariant.
QVariant DynamicRenderer::CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
const void *data, int linesize)
@@ -302,11 +339,14 @@ QVariant DynamicRenderer::CreateNativeTexture(int width, int height, int depth,
return out;
}
// Releases a backend-native texture handle.
void DynamicRenderer::DestroyNativeTexture(QVariant texture)
{
destroy_native_texture_(handle_, &texture);
}
// Releases renderer-owned backend resources before the backend object itself is
// destroyed.
void DynamicRenderer::DestroyInternal()
{
if (handle_) {
@@ -314,6 +354,7 @@ void DynamicRenderer::DestroyInternal()
}
}
// Exposes OFX OpenGL output binding through the dynamic backend when supported.
void DynamicRenderer::AttachOutputTexture(Texture *texture)
{
if (attach_output_texture_ && texture) {
@@ -322,6 +363,7 @@ void DynamicRenderer::AttachOutputTexture(Texture *texture)
}
}
// Clears any OFX output texture binding owned by the backend.
void DynamicRenderer::DetachOutputTexture()
{
if (detach_output_texture_) {
+30
View File
@@ -11,62 +11,92 @@
namespace olive
{
// C++ Renderer adapter that loads an Oak render backend shared library and
// forwards Renderer calls through the backend's C ABI.
class DynamicRenderer : public Renderer, public OpenGLContextProvider {
Q_OBJECT
public:
// Stores the requested backend name; Load() may change it after fallback.
explicit DynamicRenderer(const QString &backend, QObject *parent = nullptr);
// Destroys backend resources and unloads the dynamic library.
virtual ~DynamicRenderer() override;
using Renderer::Blit;
// Loads the backend library, resolves C ABI symbols, and creates the handle.
bool Load();
// Initializes an OpenGL backend with a caller-owned viewer context.
bool InitWithOpenGLContext(QOpenGLContext *context);
// Retrieves backend metadata through the optional info entry point.
bool GetBackendInfo(OakRenderBackendInfo *out_info) const;
// Returns the effective backend after any load-time fallback.
QString backend_name() const
{
return backend_;
}
// Initializes the backend using its default device/context path.
virtual bool Init() override;
// Runs backend post-destroy cleanup.
virtual void PostDestroy() override;
// Runs backend post-init setup.
virtual void PostInit() override;
// Clears either a native texture destination or the backend output target.
virtual void ClearDestination(Texture *texture = nullptr,
double r = 0.0, double g = 0.0,
double b = 0.0, double a = 0.0) override;
// Creates a native shader through the dynamic backend.
virtual QVariant CreateNativeShader(ShaderCode code) override;
// Destroys a native shader through the dynamic backend.
virtual void DestroyNativeShader(QVariant shader) override;
// Uploads CPU pixels to a backend texture.
virtual void UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize) override;
// Downloads backend texture pixels to CPU memory.
virtual void DownloadFromTexture(const QVariant &handle,
const VideoParams &params, void *data,
int linesize) override;
// Waits for backend work to complete.
virtual void Flush() override;
// Reads one pixel from a backend texture.
virtual Color GetPixelFromTexture(Texture *texture,
const QPointF &pt) override;
// Returns the wrapped OpenGL context for OpenGL backends.
virtual QOpenGLContext *OpenGLContext() const override;
// Reports whether the effective backend is OpenGL.
virtual bool IsOpenGL() const override;
// Attaches a texture for OFX OpenGL output when supported.
virtual void AttachOutputTexture(Texture *texture) override;
// Detaches any OFX output texture binding when supported.
virtual void DetachOutputTexture() override;
protected:
// Dispatches a shader blit through the dynamic backend.
virtual void Blit(QVariant shader, AcceleratedJob &job,
Texture *destination, VideoParams destination_params,
bool clear_destination) override;
// Allocates a native texture through the dynamic backend.
virtual QVariant CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
const void *data = nullptr,
int linesize = 0) override;
// Releases a native texture through the dynamic backend.
virtual void DestroyNativeTexture(QVariant texture) override;
// Releases backend-owned renderer resources.
virtual void DestroyInternal() override;
private:
// Resolves required backend C ABI symbols.
bool ResolveFunctions();
// Replaces a failed Vulkan backend with OpenGL.
bool FallbackToOpenGL();
// Clears all cached function pointers.
void ResetFunctions();
// Resolves the private backend library path.
QString LibraryFilename() const;
QString backend_;
+26
View File
@@ -14,14 +14,17 @@
extern "C" {
#endif
/* Opaque pointer to the backend-owned C++ renderer object. */
typedef void *OakRenderBackendHandle;
/* Identifies the concrete backend behind a dynamically loaded library. */
enum OakRenderBackendKind {
OAK_RENDER_BACKEND_UNKNOWN = 0,
OAK_RENDER_BACKEND_OPENGL = 1,
OAK_RENDER_BACKEND_VULKAN = 2
};
/* Capability bits advertised by a backend through oak_renderer_get_info(). */
enum OakRenderBackendCapability {
OAK_RENDER_BACKEND_CAP_TEXTURES = 1ULL << 0,
OAK_RENDER_BACKEND_CAP_SHADERS = 1ULL << 1,
@@ -32,6 +35,7 @@ enum OakRenderBackendCapability {
OAK_RENDER_BACKEND_CAP_DEVICE = 1ULL << 6
};
/* Static and runtime metadata returned by the backend. */
struct OakRenderBackendInfo {
uint32_t abi_version;
uint32_t kind;
@@ -40,52 +44,74 @@ struct OakRenderBackendInfo {
const char *status;
};
/* Creates a backend renderer object. */
typedef OakRenderBackendHandle (*OakBackendCreateFn)(void *parent);
/* Destroys a backend renderer object created by OakBackendCreateFn. */
typedef void (*OakBackendDestroyFn)(OakRenderBackendHandle handle);
/* Queries backend metadata and capability bits. */
typedef bool (*OakBackendGetInfoFn)(OakRenderBackendHandle handle,
struct OakRenderBackendInfo *out_info);
/* Checks whether the backend can run on the current machine. */
typedef bool (*OakBackendIsAvailableFn)(OakRenderBackendHandle handle);
/* Initializes backend-owned device/context resources. */
typedef bool (*OakBackendInitFn)(OakRenderBackendHandle handle);
/* Initializes the backend against a caller-supplied GL context when applicable. */
typedef void (*OakBackendInitWithContextFn)(OakRenderBackendHandle handle,
void *context);
/* Runs backend post-initialization after the device/context exists. */
typedef void (*OakBackendPostInitFn)(OakRenderBackendHandle handle);
/* Runs backend post-destroy cleanup before the library unloads. */
typedef void (*OakBackendPostDestroyFn)(OakRenderBackendHandle handle);
/* Destroys renderer-owned native resources. */
typedef void (*OakBackendDestroyInternalFn)(OakRenderBackendHandle handle);
/* Clears a texture destination or implicit output target. */
typedef void (*OakBackendClearDestinationFn)(OakRenderBackendHandle handle,
void *texture, double r, double g,
double b, double a);
/* Creates a native texture and writes a QVariant-compatible handle. */
typedef void (*OakBackendCreateNativeTextureFn)(OakRenderBackendHandle handle,
int width, int height, int depth,
int format, int channel_count,
const void *data, int linesize,
void *out_variant);
/* Destroys a native texture represented by a QVariant-compatible handle. */
typedef void (*OakBackendDestroyNativeTextureFn)(OakRenderBackendHandle handle,
const void *variant);
/* Creates a native shader and writes a QVariant-compatible handle. */
typedef void (*OakBackendCreateNativeShaderFn)(OakRenderBackendHandle handle,
const void *shader_code,
void *out_variant);
/* Destroys a native shader represented by a QVariant-compatible handle. */
typedef void (*OakBackendDestroyNativeShaderFn)(OakRenderBackendHandle handle,
const void *variant);
/* Uploads CPU pixel data to a native texture. */
typedef void (*OakBackendUploadToTextureFn)(OakRenderBackendHandle handle,
const void *variant,
const void *video_params,
const void *data, int linesize);
/* Downloads native texture pixels into caller-owned CPU memory. */
typedef void (*OakBackendDownloadFromTextureFn)(OakRenderBackendHandle handle,
const void *variant,
const void *video_params,
void *data, int linesize);
/* Waits for backend work that must be visible to later operations. */
typedef void (*OakBackendFlushFn)(OakRenderBackendHandle handle);
/* Reads one pixel from a texture. */
typedef void (*OakBackendGetPixelFromTextureFn)(OakRenderBackendHandle handle,
void *texture, const void *point,
void *out_color);
/* Executes a shader blit job. */
typedef void (*OakBackendBlitFn)(OakRenderBackendHandle handle,
const void *shader, void *job,
void *destination,
const void *destination_params,
bool clear_destination);
/* Attaches an output texture for OFX OpenGL rendering when supported. */
typedef void (*OakBackendAttachOutputTextureFn)(OakRenderBackendHandle handle,
const void *texture_id);
/* Detaches an OFX output texture when supported. */
typedef void (*OakBackendDetachOutputTextureFn)(OakRenderBackendHandle handle);
/* Returns the backend OpenGL context, or null for non-OpenGL backends. */
typedef void *(*OakBackendOpenGLContextFn)(OakRenderBackendHandle handle);
#ifdef __cplusplus
+26
View File
@@ -23,11 +23,14 @@ public:
using olive::OpenGLRenderer::DetachTextureAsDestination;
};
// Converts the opaque C ABI handle back to the C++ renderer used internally.
BackendOpenGLRenderer *Renderer(OakRenderBackendHandle handle)
{
return static_cast<BackendOpenGLRenderer *>(handle);
}
// Interprets ABI QVariant payloads without copying; both modules are built
// against the same Qt/C++ ABI in this first-generation dynamic backend.
const QVariant &VariantRef(const void *variant)
{
return *static_cast<const QVariant *>(variant);
@@ -35,16 +38,19 @@ const QVariant &VariantRef(const void *variant)
} // namespace
// Creates the backend object and returns it as an opaque C handle.
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle oak_renderer_create(void *parent)
{
return new BackendOpenGLRenderer(static_cast<QObject *>(parent));
}
// Destroys the opaque backend object created by oak_renderer_create().
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handle)
{
delete Renderer(handle);
}
// Reports static OpenGL backend capabilities to the adapter.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
OakRenderBackendHandle handle, OakRenderBackendInfo *out_info)
{
@@ -62,39 +68,47 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
return true;
}
// OpenGL availability is context-dependent, so object creation is the minimum
// availability signal for this backend.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
OakRenderBackendHandle handle)
{
return handle != nullptr;
}
// Initializes an offscreen OpenGL context for non-viewer users.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
{
return Renderer(handle)->Init();
}
// Initializes the backend against a caller-owned viewer OpenGL context.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
OakRenderBackendHandle handle, void *context)
{
Renderer(handle)->Init(static_cast<QOpenGLContext *>(context));
}
// Runs renderer post-initialization once the GL context is available.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_init(OakRenderBackendHandle handle)
{
Renderer(handle)->PostInit();
}
// Releases post-init OpenGL surface/context state.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_destroy(OakRenderBackendHandle handle)
{
Renderer(handle)->PostDestroy();
}
// Releases renderer-owned GL resources before object destruction.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_internal(
OakRenderBackendHandle handle)
{
Renderer(handle)->DestroyInternal();
}
// Clears either the widget framebuffer or a texture destination.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
OakRenderBackendHandle handle, void *texture, double r, double g, double b,
double a)
@@ -103,6 +117,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
r, g, b, a);
}
// Creates an OpenGL texture and writes its QVariant handle to out_variant.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_texture(
OakRenderBackendHandle handle, int width, int height, int depth, int format,
int channel_count, const void *data, int linesize, void *out_variant)
@@ -112,12 +127,14 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_texture(
channel_count, data, linesize);
}
// Destroys an OpenGL texture represented by a QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_texture(
OakRenderBackendHandle handle, const void *variant)
{
Renderer(handle)->DestroyNativeTexture(VariantRef(variant));
}
// Compiles an OpenGL shader program and returns its QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
OakRenderBackendHandle handle, const void *shader_code, void *out_variant)
{
@@ -125,12 +142,14 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
*static_cast<const olive::ShaderCode *>(shader_code));
}
// Destroys an OpenGL shader program represented by a QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_shader(
OakRenderBackendHandle handle, const void *variant)
{
Renderer(handle)->DestroyNativeShader(VariantRef(variant));
}
// Uploads CPU pixel data into an OpenGL texture.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
const void *data, int linesize)
@@ -140,6 +159,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
data, linesize);
}
// Reads an OpenGL texture back to CPU memory.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
void *data, int linesize)
@@ -149,11 +169,13 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
data, linesize);
}
// Flushes/waits for pending OpenGL work as required by the renderer.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_flush(OakRenderBackendHandle handle)
{
Renderer(handle)->Flush();
}
// Reads one pixel from an OpenGL texture.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
OakRenderBackendHandle handle, void *texture, const void *point,
void *out_color)
@@ -162,6 +184,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
static_cast<olive::Texture *>(texture), *static_cast<const QPointF *>(point));
}
// Executes a shader blit through the wrapped C++ OpenGL renderer.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
OakRenderBackendHandle handle, const void *shader, void *job,
void *destination, const void *destination_params, bool clear_destination)
@@ -173,18 +196,21 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
clear_destination);
}
// Exposes the wrapped OpenGL context for GL-specific integrations.
OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
OakRenderBackendHandle handle)
{
return Renderer(handle)->context();
}
// Binds an output texture for OFX OpenGL rendering.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
OakRenderBackendHandle handle, const void *texture_id)
{
Renderer(handle)->AttachTextureAsDestination(VariantRef(texture_id));
}
// Detaches any OFX OpenGL output texture binding.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_detach_output_texture(
OakRenderBackendHandle handle)
{
+6
View File
@@ -408,6 +408,8 @@ void OpenGLRenderer::Flush()
}
}
// Adapts the generic Renderer output attachment hook to OpenGL's framebuffer
// attachment path used by OFX OpenGL rendering.
void OpenGLRenderer::AttachOutputTexture(olive::Texture *texture)
{
if (texture) {
@@ -415,6 +417,7 @@ void OpenGLRenderer::AttachOutputTexture(olive::Texture *texture)
}
}
// Clears the framebuffer attachment installed by AttachOutputTexture().
void OpenGLRenderer::DetachOutputTexture()
{
DetachTextureAsDestination();
@@ -987,6 +990,9 @@ bool OpenGLRenderer::EnsureContextCurrent(const char *caller)
return false;
}
// QOpenGLContext may only be made current from its owning thread. Viewer
// paint code can receive textures produced by a render-thread OpenGL
// renderer, so guard here before makeCurrent() can crash inside Qt/GL.
if (context_->thread() != QThread::currentThread()) {
qWarning() << caller << "called from the wrong thread for this OpenGL context";
return false;
+26
View File
@@ -22,11 +22,14 @@ public:
using olive::VulkanRenderer::DestroyNativeTexture;
};
// Converts the opaque C ABI handle back to the C++ Vulkan renderer.
BackendVulkanRenderer *Renderer(OakRenderBackendHandle handle)
{
return static_cast<BackendVulkanRenderer *>(handle);
}
// Interprets ABI QVariant payloads without copying; this ABI version assumes
// the host and backend are built with the same Qt/C++ ABI.
const QVariant &VariantRef(const void *variant)
{
return *static_cast<const QVariant *>(variant);
@@ -34,16 +37,19 @@ const QVariant &VariantRef(const void *variant)
} // namespace
// Creates the Vulkan backend object and returns it as an opaque C handle.
OAK_RENDER_BACKEND_EXPORT OakRenderBackendHandle oak_renderer_create(void *parent)
{
return new BackendVulkanRenderer(static_cast<QObject *>(parent));
}
// Destroys the opaque backend object created by oak_renderer_create().
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy(OakRenderBackendHandle handle)
{
delete Renderer(handle);
}
// Reports Vulkan backend capabilities and runtime availability status.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
OakRenderBackendHandle handle, OakRenderBackendInfo *out_info)
{
@@ -60,6 +66,8 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_get_info(
return true;
}
// Probes runtime availability by trying Init() once; this lets missing ICDs or
// unusable drivers fall back before normal rendering starts.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
OakRenderBackendHandle handle)
{
@@ -74,11 +82,13 @@ OAK_RENDER_BACKEND_EXPORT bool oak_renderer_is_available(
return r->IsAvailable();
}
// Initializes the Vulkan device path.
OAK_RENDER_BACKEND_EXPORT bool oak_renderer_init(OakRenderBackendHandle handle)
{
return Renderer(handle)->Init();
}
// Vulkan does not use a QOpenGLContext; the argument is accepted for ABI parity.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
OakRenderBackendHandle handle, void *context)
{
@@ -86,24 +96,28 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_init_with_context(
Renderer(handle)->Init();
}
// Creates reusable Vulkan resources after device initialization.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_init(
OakRenderBackendHandle handle)
{
Renderer(handle)->PostInit();
}
// Reserved for API symmetry; Vulkan cleanup is handled by destroy_internal.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_post_destroy(
OakRenderBackendHandle handle)
{
Renderer(handle)->PostDestroy();
}
// Releases all Vulkan resources owned by the renderer.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_internal(
OakRenderBackendHandle handle)
{
Renderer(handle)->DestroyInternal();
}
// Clears a Vulkan texture destination.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
OakRenderBackendHandle handle, void *texture, double r, double g, double b,
double a)
@@ -112,6 +126,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_clear_destination(
r, g, b, a);
}
// Creates a Vulkan texture and writes its QVariant handle to out_variant.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_texture(
OakRenderBackendHandle handle, int width, int height, int depth, int format,
int channel_count, const void *data, int linesize, void *out_variant)
@@ -121,12 +136,14 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_texture(
channel_count, data, linesize);
}
// Destroys a Vulkan texture represented by a QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_texture(
OakRenderBackendHandle handle, const void *variant)
{
Renderer(handle)->DestroyNativeTexture(VariantRef(variant));
}
// Compiles a Vulkan shader and returns its QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
OakRenderBackendHandle handle, const void *shader_code, void *out_variant)
{
@@ -134,12 +151,14 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_create_native_shader(
*static_cast<const olive::ShaderCode *>(shader_code));
}
// Destroys a Vulkan shader represented by a QVariant handle.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_destroy_native_shader(
OakRenderBackendHandle handle, const void *variant)
{
Renderer(handle)->DestroyNativeShader(VariantRef(variant));
}
// Uploads CPU pixel data into a Vulkan texture.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
const void *data, int linesize)
@@ -149,6 +168,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_upload_to_texture(
data, linesize);
}
// Downloads a Vulkan texture to CPU memory.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
OakRenderBackendHandle handle, const void *variant, const void *video_params,
void *data, int linesize)
@@ -158,11 +178,13 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_download_from_texture(
data, linesize);
}
// Waits for all queued Vulkan work to finish.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_flush(OakRenderBackendHandle handle)
{
Renderer(handle)->Flush();
}
// Reads one pixel from a Vulkan texture.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
OakRenderBackendHandle handle, void *texture, const void *point,
void *out_color)
@@ -171,6 +193,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_get_pixel_from_texture(
static_cast<olive::Texture *>(texture), *static_cast<const QPointF *>(point));
}
// Executes a shader blit through the wrapped Vulkan renderer.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
OakRenderBackendHandle handle, const void *shader, void *job,
void *destination, const void *destination_params, bool clear_destination)
@@ -182,6 +205,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_blit(
clear_destination);
}
// Vulkan has no OpenGL context; return null so callers avoid GL-only paths.
OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
OakRenderBackendHandle handle)
{
@@ -189,6 +213,7 @@ OAK_RENDER_BACKEND_EXPORT void *oak_renderer_opengl_context(
return nullptr;
}
// OFX OpenGL output attachment is unsupported in Vulkan and intentionally no-op.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
OakRenderBackendHandle handle, const void *texture_id)
{
@@ -197,6 +222,7 @@ OAK_RENDER_BACKEND_EXPORT void oak_renderer_attach_output_texture(
// Vulkan does not support OFX OpenGL render output attachment.
}
// OFX OpenGL output detachment is unsupported in Vulkan and intentionally no-op.
OAK_RENDER_BACKEND_EXPORT void oak_renderer_detach_output_texture(
OakRenderBackendHandle handle)
{
+75
View File
@@ -60,16 +60,21 @@ static const float kBlitVertices[] = {
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
};
// Constructs the renderer; Vulkan resources are created lazily so unavailable
// Vulkan systems can still instantiate the object and report fallback state.
VulkanRenderer::VulkanRenderer(QObject *parent) : Renderer(parent)
{
}
// Ensures Vulkan resources are destroyed before the QObject hierarchy goes away.
VulkanRenderer::~VulkanRenderer()
{
Destroy();
PostDestroy();
}
// Initializes the Vulkan instance/device path once. Repeated calls are accepted
// because backend availability probes may call Init() before normal rendering.
bool VulkanRenderer::Init()
{
if (instance_ != VK_NULL_HANDLE) {
@@ -79,6 +84,7 @@ bool VulkanRenderer::Init()
CreateDescriptorPool();
}
// Creates reusable draw resources after Init() has a valid logical device.
void VulkanRenderer::PostInit()
{
if (vertex_buffer_ != VK_NULL_HANDLE) {
@@ -89,10 +95,15 @@ void VulkanRenderer::PostInit()
CreateNearestSampler();
}
// Reserved for Renderer API symmetry; Vulkan teardown is centralized in
// DestroyInternal() so object destruction and explicit Destroy() share a path.
void VulkanRenderer::PostDestroy()
{
}
// Destroys all Vulkan objects in dependency order. The device is idled first so
// cached textures, pipelines, descriptor pools, and command pools are no longer
// referenced by in-flight work.
void VulkanRenderer::DestroyInternal()
{
if (device_ != VK_NULL_HANDLE) {
@@ -185,6 +196,7 @@ void VulkanRenderer::DestroyInternal()
}
}
// Creates the minimal Vulkan instance needed for offscreen rendering.
bool VulkanRenderer::CreateInstance()
{
VkApplicationInfo app_info = {};
@@ -208,6 +220,8 @@ bool VulkanRenderer::CreateInstance()
return true;
}
// Selects the first physical device with a graphics queue and creates a logical
// device without swapchain extensions because viewer output is CPU readback.
bool VulkanRenderer::CreateDevice()
{
VkResult result = vkEnumeratePhysicalDevices(instance_, &physical_device_count_, nullptr);
@@ -284,6 +298,8 @@ bool VulkanRenderer::CreateDevice()
return true;
}
// Creates the command pool used for short-lived transfer and draw command
// buffers.
bool VulkanRenderer::CreateCommandPool()
{
VkCommandPoolCreateInfo pool_info = {};
@@ -299,6 +315,8 @@ bool VulkanRenderer::CreateCommandPool()
return true;
}
// Creates a pool large enough for transient per-blit descriptor sets. Descriptor
// sets are freed after each pass, so this is capacity rather than lifetime count.
bool VulkanRenderer::CreateDescriptorPool()
{
VkDescriptorPoolSize pool_sizes[2] = {};
@@ -323,6 +341,7 @@ bool VulkanRenderer::CreateDescriptorPool()
return true;
}
// Returns a cached render pass keyed by color format and load operation.
VkRenderPass VulkanRenderer::GetOrCreateRenderPass(VkFormat format, bool clear)
{
const quint64 key = (static_cast<quint64>(format) << 1) | (clear ? 1ULL : 0ULL);
@@ -387,6 +406,7 @@ VkRenderPass VulkanRenderer::GetOrCreateRenderPass(VkFormat format, bool clear)
}
// Uploads a fullscreen quad to device-local memory through a staging buffer.
bool VulkanRenderer::CreateVertexBuffer()
{
VkDeviceSize buffer_size = sizeof(kBlitVertices);
@@ -486,6 +506,7 @@ bool VulkanRenderer::CreateVertexBuffer()
return true;
}
// Creates the persistent linear sampler shared by all texture bindings.
bool VulkanRenderer::CreateLinearSampler()
{
VkSamplerCreateInfo sampler_info = {};
@@ -512,6 +533,7 @@ bool VulkanRenderer::CreateLinearSampler()
return true;
}
// Creates the persistent nearest sampler shared by all texture bindings.
bool VulkanRenderer::CreateNearestSampler()
{
VkSamplerCreateInfo sampler_info = {};
@@ -538,6 +560,7 @@ bool VulkanRenderer::CreateNearestSampler()
return true;
}
// Maps Oak interpolation settings to persistent Vulkan sampler objects.
VkSampler VulkanRenderer::GetSampler(Texture::Interpolation interpolation) const
{
switch (interpolation) {
@@ -550,6 +573,7 @@ VkSampler VulkanRenderer::GetSampler(Texture::Interpolation interpolation) const
}
}
// Allocates host-visible coherent memory for one upload/download transfer.
bool VulkanRenderer::CreateStagingBuffer(VkDeviceSize size, VkBuffer *out_buffer,
VkDeviceMemory *out_memory)
{
@@ -596,6 +620,7 @@ bool VulkanRenderer::CreateStagingBuffer(VkDeviceSize size, VkBuffer *out_buffer
return true;
}
// Releases a staging buffer and its memory allocation.
void VulkanRenderer::DestroyStagingBuffer(VkBuffer buffer, VkDeviceMemory memory)
{
if (buffer != VK_NULL_HANDLE) {
@@ -606,6 +631,7 @@ void VulkanRenderer::DestroyStagingBuffer(VkBuffer buffer, VkDeviceMemory memory
}
}
// Starts a primary command buffer intended for immediate submit-and-wait use.
VkCommandBuffer VulkanRenderer::BeginOneTimeCommands()
{
VkCommandBufferAllocateInfo alloc_info = {};
@@ -625,6 +651,7 @@ VkCommandBuffer VulkanRenderer::BeginOneTimeCommands()
return cmd;
}
// Submits a one-time command buffer and waits synchronously for completion.
void VulkanRenderer::EndOneTimeCommands(VkCommandBuffer cmd)
{
vkEndCommandBuffer(cmd);
@@ -640,6 +667,8 @@ void VulkanRenderer::EndOneTimeCommands(VkCommandBuffer cmd)
vkFreeCommandBuffers(device_, command_pool_, 1, &cmd);
}
// Emits a conservative barrier for the image layout transitions used by this
// renderer: upload, shader read, color attachment, clear, and readback.
void VulkanRenderer::TransitionImageLayout(VkCommandBuffer cmd, VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout)
@@ -748,6 +777,7 @@ void VulkanRenderer::TransitionImageLayout(VkCommandBuffer cmd, VkImage image,
}
// Records a buffer-to-image copy for tightly packed texture uploads.
void VulkanRenderer::CopyBufferToImage(VkCommandBuffer cmd, VkBuffer buffer,
VkImage image, uint32_t width,
uint32_t height, uint32_t depth)
@@ -767,6 +797,7 @@ void VulkanRenderer::CopyBufferToImage(VkCommandBuffer cmd, VkBuffer buffer,
1, &region);
}
// Records an image-to-buffer copy for full texture downloads or one-pixel reads.
void VulkanRenderer::CopyImageToBuffer(VkCommandBuffer cmd, VkImage image,
VkBuffer buffer, uint32_t width,
uint32_t height,
@@ -787,6 +818,7 @@ void VulkanRenderer::CopyImageToBuffer(VkCommandBuffer cmd, VkImage image,
1, &region);
}
// Converts Oak's pixel format/channel count pair to the closest Vulkan format.
VkFormat VulkanRenderer::PixelFormatToVkFormat(PixelFormat format,
int channel_count) const
{
@@ -830,6 +862,7 @@ VkFormat VulkanRenderer::PixelFormatToVkFormat(PixelFormat format,
return VK_FORMAT_UNDEFINED;
}
// Checks color-attachment support before selecting renderable image formats.
bool VulkanRenderer::IsColorAttachmentSupported(VkFormat format) const
{
VkFormatProperties props;
@@ -838,6 +871,8 @@ bool VulkanRenderer::IsColorAttachmentSupported(VkFormat format) const
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
}
// Chooses a renderable Vulkan format and falls back from RGB to RGBA when a
// driver does not expose 3-channel color attachment support.
VkFormat VulkanRenderer::PickRenderableFormat(PixelFormat format,
int channel_count) const
{
@@ -858,6 +893,7 @@ VkFormat VulkanRenderer::PickRenderableFormat(PixelFormat format,
return VK_FORMAT_UNDEFINED;
}
// Returns the packed texel size for the VkFormat values generated above.
int VulkanRenderer::GetVkFormatBytesPerPixel(VkFormat format) const
{
switch (format) {
@@ -898,6 +934,7 @@ int VulkanRenderer::GetVkFormatBytesPerPixel(VkFormat format) const
}
}
// Returns the alpha fill value used when expanding formats without alpha.
float VulkanRenderer::GetFormatMaxAlpha(PixelFormat format) const
{
if (format == PixelFormat::U8) {
@@ -908,6 +945,8 @@ float VulkanRenderer::GetFormatMaxAlpha(PixelFormat format) const
return 1.0f;
}
// Copies tightly-packed pixels while changing channel count. This handles the
// common Vulkan fallback where requested RGB data is stored as RGBA on the GPU.
void VulkanRenderer::CopyPixelsWithChannelConversion(const void *src, void *dst,
int width, int height, int depth,
int src_channels, int dst_channels,
@@ -958,12 +997,14 @@ void VulkanRenderer::CopyPixelsWithChannelConversion(const void *src, void *dst,
}
}
// Rounds size up to the next multiple of alignment.
VkDeviceSize VulkanRenderer::AlignSize(VkDeviceSize size,
VkDeviceSize alignment) const
{
return (size + alignment - 1) & ~(alignment - 1);
}
// Finds a compatible memory type satisfying Vulkan's bitmask and property flags.
uint32_t VulkanRenderer::FindMemoryType(uint32_t type_filter,
VkMemoryPropertyFlags properties) const
{
@@ -977,6 +1018,7 @@ uint32_t VulkanRenderer::FindMemoryType(uint32_t type_filter,
return UINT32_MAX;
}
// Creates a Vulkan image, memory allocation, and image view for an Oak texture.
QVariant VulkanRenderer::CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
const void *data, int linesize)
@@ -1164,6 +1206,7 @@ QVariant VulkanRenderer::CreateNativeTexture(int width, int height, int depth,
return QVariant::fromValue(tex->id);
}
// Destroys a texture handle and all Vulkan objects owned by that texture.
void VulkanRenderer::DestroyNativeTexture(QVariant texture)
{
QMutexLocker lock(&mutex_);
@@ -1187,6 +1230,8 @@ void VulkanRenderer::DestroyNativeTexture(QVariant texture)
delete tex;
}
// Uploads CPU pixels to an existing image. The staging layout is based on the
// selected GPU VkFormat, then CPU data is repacked when channel counts differ.
void VulkanRenderer::UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize)
@@ -1273,6 +1318,8 @@ void VulkanRenderer::UploadToTexture(const QVariant &handle,
tex->current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
// Downloads an image to CPU memory. When the GPU format is wider than the
// requested CPU format, the staging data is compacted back to the caller layout.
void VulkanRenderer::DownloadFromTexture(const QVariant &handle,
const VideoParams &params, void *data,
int linesize)
@@ -1355,6 +1402,7 @@ void VulkanRenderer::DownloadFromTexture(const QVariant &handle,
tex->current_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
}
// Blocks until the device is idle so later CPU readback or teardown is safe.
void VulkanRenderer::Flush()
{
if (device_ != VK_NULL_HANDLE) {
@@ -1362,6 +1410,8 @@ void VulkanRenderer::Flush()
}
}
// Clears a texture with vkCmdClearColorImage; null destinations are ignored
// because this backend has no implicit swapchain framebuffer.
void VulkanRenderer::ClearDestination(olive::Texture *texture, double r, double g,
double b, double a)
{
@@ -1399,6 +1449,7 @@ void VulkanRenderer::ClearDestination(olive::Texture *texture, double r, double
EndOneTimeCommands(cmd);
}
// Reads one pixel by copying a 1x1 image region into a staging buffer.
Color VulkanRenderer::GetPixelFromTexture(olive::Texture *texture,
const QPointF &pt)
{
@@ -1465,6 +1516,7 @@ Color VulkanRenderer::GetPixelFromTexture(olive::Texture *texture,
// Shader compilation (GLSL -> SPIR-V via shaderc)
// ------------------------------------------------------------------
// Returns true for GLSL sampler uniforms that must become explicit descriptors.
static bool IsSamplerType(const QString &type)
{
static const QRegularExpression sampler_re(
@@ -1472,6 +1524,8 @@ static bool IsSamplerType(const QString &type)
return sampler_re.match(type).hasMatch();
}
// Ensures GLSL has a Vulkan-compatible version directive before shaderc compiles
// it as GLSL 450.
QString VulkanRenderer::EnsureGlslVersion450(const QString &glsl) const
{
QString result = glsl.trimmed();
@@ -1483,6 +1537,9 @@ QString VulkanRenderer::EnsureGlslVersion450(const QString &glsl) const
return QStringLiteral("#version 450 core\n") + result;
}
// Converts legacy Oak/OpenGL GLSL into Vulkan GLSL. The conversion keeps shader
// semantics but replaces implicit attributes/varyings and texture sampling with
// explicit layouts that Vulkan requires.
QString VulkanRenderer::ConvertGlslToVulkan(const QString &glsl,
VkShaderStageFlagBits stage)
{
@@ -1513,6 +1570,7 @@ QString VulkanRenderer::ConvertGlslToVulkan(const QString &glsl,
return result;
}
// Returns the std140 storage size for scalar, vector, color, and matrix values.
VkDeviceSize VulkanRenderer::GetStd140Size(const QString &type) const
{
if (type == QStringLiteral("float")) return 4;
@@ -1524,6 +1582,7 @@ VkDeviceSize VulkanRenderer::GetStd140Size(const QString &type) const
return 4;
}
// Returns std140 base alignment so generated UBO offsets match GPU layout rules.
VkDeviceSize VulkanRenderer::GetStd140Alignment(const QString &type) const
{
if (type == QStringLiteral("float")) return 4;
@@ -1535,6 +1594,8 @@ VkDeviceSize VulkanRenderer::GetStd140Alignment(const QString &type) const
return 4;
}
// Scans GLSL uniform declarations and splits them into samplers and values. This
// is intentionally narrow and targets the shader style generated by Oak nodes.
void VulkanRenderer::ExtractUniforms(const QString &glsl,
QVector<UniformInfo> *out_uniforms,
QVector<QString> *out_samplers) const
@@ -1573,6 +1634,7 @@ void VulkanRenderer::ExtractUniforms(const QString &glsl,
}
}
// Computes std140 offsets in declaration order and records the total UBO size.
void VulkanRenderer::ComputeUniformLayout(QVector<UniformInfo> *uniforms) const
{
VkDeviceSize offset = 0;
@@ -1585,6 +1647,7 @@ void VulkanRenderer::ComputeUniformLayout(QVector<UniformInfo> *uniforms) const
}
}
// Generates the uniform block source inserted into rewritten shaders.
QString VulkanRenderer::BuildUboBlock(const QVector<UniformInfo> &uniforms) const
{
if (uniforms.isEmpty()) {
@@ -1599,6 +1662,8 @@ QString VulkanRenderer::BuildUboBlock(const QVector<UniformInfo> &uniforms) cons
return ubo;
}
// Rewrites GLSL so non-sampler uniforms live in set=0,binding=0 and sampler
// uniforms get deterministic explicit bindings after the UBO.
QString VulkanRenderer::RewriteShaderWithUbo(
const QString &glsl,
const QVector<UniformInfo> &all_uniforms,
@@ -1658,6 +1723,8 @@ QString VulkanRenderer::RewriteShaderWithUbo(
}
// Compiles Vulkan GLSL into SPIR-V using shaderc. Without shaderc this backend
// can initialize but cannot create shaders.
bool VulkanRenderer::CompileGlslToSpv(const QString &glsl,
VkShaderStageFlagBits stage,
QByteArray *out_spv)
@@ -1724,6 +1791,7 @@ bool VulkanRenderer::CompileGlslToSpv(const QString &glsl,
#endif
}
// Converts, compiles, and stores a shader pair plus descriptor metadata.
QVariant VulkanRenderer::CreateNativeShader(olive::ShaderCode code)
{
QMutexLocker lock(&mutex_);
@@ -1893,6 +1961,7 @@ QVariant VulkanRenderer::CreateNativeShader(olive::ShaderCode code)
}
// Releases shader modules, descriptor layout, pipeline layout, and pipelines.
void VulkanRenderer::DestroyNativeShader(QVariant shader)
{
QMutexLocker lock(&mutex_);
@@ -1922,6 +1991,8 @@ void VulkanRenderer::DestroyNativeShader(QVariant shader)
delete sh;
}
// Creates a graphics pipeline for the destination render format. Viewport and
// scissor are dynamic so one pipeline can handle multiple target sizes.
bool VulkanRenderer::CreatePipelineForShader(VulkanShader *shader,
const VideoParams &dest_params,
@@ -2045,6 +2116,8 @@ bool VulkanRenderer::CreatePipelineForShader(VulkanShader *shader,
return true;
}
// Executes one fullscreen draw pass. Texture descriptors and a transient UBO are
// allocated per pass so iterative shaders can update bindings cheaply.
void VulkanRenderer::BlitPass(VulkanShader *shader, VulkanTexture *dest_tex,
const QVector<TextureBinding> &bindings,
const QByteArray &ubo_data,
@@ -2257,6 +2330,8 @@ void VulkanRenderer::BlitPass(VulkanShader *shader, VulkanTexture *dest_tex,
}
}
// Runs a shader job. Multi-iteration jobs ping-pong between temporary textures
// and replace the configured iterative input with the previous pass output.
void VulkanRenderer::Blit(QVariant shader_variant, olive::AcceleratedJob &a_job,
olive::Texture *destination,
VideoParams destination_params,
+59
View File
@@ -35,29 +35,45 @@ namespace olive
class VulkanRenderer : public Renderer {
Q_OBJECT
public:
// Creates a renderer object; Vulkan objects are created lazily in Init().
explicit VulkanRenderer(QObject *parent = nullptr);
// Releases Vulkan objects through the normal Renderer destruction path.
virtual ~VulkanRenderer() override;
// Creates the Vulkan instance, logical device, command pool, and descriptor
// pool required for offscreen rendering.
virtual bool Init() override;
// Creates reusable GPU resources that require a fully initialized device.
virtual void PostInit() override;
// Reserved for symmetry with OpenGLRenderer; Vulkan cleanup is handled by
// DestroyInternal().
virtual void PostDestroy() override;
// Clears either a texture render target or the currently bound output target.
virtual void ClearDestination(olive::Texture *texture = nullptr,
double r = 0.0, double g = 0.0,
double b = 0.0, double a = 0.0) override;
// Compiles GLSL to SPIR-V, creates shader modules, and prepares descriptor
// metadata for later blits.
virtual QVariant CreateNativeShader(olive::ShaderCode code) override;
// Destroys shader modules, descriptor layout, pipeline layout, and cached
// pipelines associated with a shader handle.
virtual void DestroyNativeShader(QVariant shader) override;
// Uploads CPU pixel data to a Vulkan image via a staging buffer.
virtual void UploadToTexture(const QVariant &handle,
const VideoParams &params, const void *data,
int linesize) override;
// Downloads a Vulkan image to CPU memory via a staging buffer.
virtual void DownloadFromTexture(const QVariant &handle,
const VideoParams &params, void *data,
int linesize) override;
// Waits for outstanding device work to complete.
virtual void Flush() override;
// Reads a single texture pixel using a one-pixel transfer readback.
virtual Color GetPixelFromTexture(olive::Texture *texture,
const QPointF &pt) override;
@@ -67,14 +83,19 @@ public:
}
protected:
// Runs one or more fullscreen shader passes into the destination texture.
virtual void Blit(QVariant shader, olive::AcceleratedJob &job,
olive::Texture *destination, VideoParams destination_params,
bool clear_destination) override;
// Creates a Vulkan image/view/memory bundle and optionally uploads initial
// pixel data.
virtual QVariant CreateNativeTexture(int width, int height, int depth,
PixelFormat format, int channel_count,
const void *data = nullptr,
int linesize = 0) override;
// Releases a Vulkan texture bundle.
virtual void DestroyNativeTexture(QVariant texture) override;
// Releases all Vulkan device resources owned by this renderer.
virtual void DestroyInternal() override;
private:
@@ -82,62 +103,99 @@ private:
struct VulkanShader;
struct UniformInfo;
// Creates the Vulkan instance used for all offscreen work.
bool CreateInstance();
// Chooses a graphics-capable physical device and creates the logical device.
bool CreateDevice();
// Creates a command pool for short-lived command buffers.
bool CreateCommandPool();
// Creates the descriptor pool used for per-blit UBO/sampler sets.
bool CreateDescriptorPool();
// Uploads the fullscreen quad vertex buffer used by BlitPass().
bool CreateVertexBuffer();
// Creates the persistent linear sampler.
bool CreateLinearSampler();
// Creates the persistent nearest-neighbor sampler.
bool CreateNearestSampler();
// Returns the persistent sampler matching the requested interpolation mode.
VkSampler GetSampler(Texture::Interpolation interpolation) const;
// Allocates a host-visible staging buffer for upload/download transfers.
bool CreateStagingBuffer(VkDeviceSize size, VkBuffer *out_buffer,
VkDeviceMemory *out_memory);
// Destroys a staging buffer pair allocated by CreateStagingBuffer().
void DestroyStagingBuffer(VkBuffer buffer, VkDeviceMemory memory);
// Begins a one-shot command buffer and records it immediately.
VkCommandBuffer BeginOneTimeCommands();
// Submits and waits for a one-shot command buffer.
void EndOneTimeCommands(VkCommandBuffer cmd);
// Emits an image memory barrier for the subset of layouts this renderer uses.
void TransitionImageLayout(VkCommandBuffer cmd, VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout);
// Records a tightly packed buffer-to-image copy.
void CopyBufferToImage(VkCommandBuffer cmd, VkBuffer buffer, VkImage image,
uint32_t width, uint32_t height, uint32_t depth);
// Records an image-to-buffer copy, optionally reading one pixel offset.
void CopyImageToBuffer(VkCommandBuffer cmd, VkImage image, VkBuffer buffer,
uint32_t width, uint32_t height,
uint32_t offset_x = 0, uint32_t offset_y = 0);
// Converts Oak pixel format/channel metadata to a preferred Vulkan format.
VkFormat PixelFormatToVkFormat(PixelFormat format, int channel_count) const;
// Picks a color-attachment-capable format, falling back from RGB to RGBA
// where drivers do not support 3-channel render targets.
VkFormat PickRenderableFormat(PixelFormat format, int channel_count) const;
// Checks whether a format can be used as a render target.
bool IsColorAttachmentSupported(VkFormat format) const;
// Returns the packed byte size for supported VkFormat values.
int GetVkFormatBytesPerPixel(VkFormat format) const;
// Returns the alpha fill value used when expanding RGB data to RGBA.
float GetFormatMaxAlpha(PixelFormat format) const;
// Repackages tightly packed pixels when the requested CPU channel count
// differs from the selected GPU format channel count.
void CopyPixelsWithChannelConversion(const void *src, void *dst,
int width, int height, int depth,
int src_channels, int dst_channels,
PixelFormat format) const;
// Rounds a size up to the requested alignment.
VkDeviceSize AlignSize(VkDeviceSize size, VkDeviceSize alignment) const;
// Finds a Vulkan memory type matching the requested properties.
uint32_t FindMemoryType(uint32_t type_filter,
VkMemoryPropertyFlags properties) const;
// Compiles GLSL source into SPIR-V using shaderc when available.
bool CompileGlslToSpv(const QString &glsl, VkShaderStageFlagBits stage,
QByteArray *out_spv);
// Rewrites an Oak GLSL shader into Vulkan-compatible GLSL.
QString ConvertGlslToVulkan(const QString &glsl, VkShaderStageFlagBits stage);
// Ensures a shader declares a Vulkan-compatible GLSL version.
QString EnsureGlslVersion450(const QString &glsl) const;
// Extracts uniforms and sampler names from GLSL declarations.
void ExtractUniforms(const QString &glsl, QVector<UniformInfo> *out_uniforms,
QVector<QString> *out_samplers) const;
// Computes std140 offsets and total UBO size for extracted uniforms.
void ComputeUniformLayout(QVector<UniformInfo> *uniforms) const;
// Builds the generated uniform block used by rewritten shaders.
QString BuildUboBlock(const QVector<UniformInfo> &uniforms) const;
// Rewrites standalone uniforms and samplers into explicit UBO/sampler
// bindings accepted by Vulkan GLSL.
QString RewriteShaderWithUbo(const QString &glsl,
const QVector<UniformInfo> &all_uniforms,
const QHash<QString, int> &sampler_bindings) const;
// Returns std140 storage size for a supported GLSL type.
VkDeviceSize GetStd140Size(const QString &type) const;
// Returns std140 alignment for a supported GLSL type.
VkDeviceSize GetStd140Alignment(const QString &type) const;
// Creates or retrieves the graphics pipeline for a shader/render format pair.
bool CreatePipelineForShader(VulkanShader *shader,
const VideoParams &dest_params,
VkFormat render_pass_format);
// Caches simple single-color-attachment render passes by format/clear mode.
VkRenderPass GetOrCreateRenderPass(VkFormat format, bool clear);
struct TextureBinding {
@@ -146,6 +204,7 @@ private:
Texture::Interpolation interp;
};
// Executes one fullscreen pass with the provided texture bindings and UBO.
void BlitPass(VulkanShader *shader, VulkanTexture *dest_tex,
const QVector<TextureBinding> &bindings,
const QByteArray &ubo_data,