feat: Implement SDL2 Renderer as Concrete IRenderer Implementation
- Added IRenderer interface to define abstract rendering operations, decoupling application logic from specific rendering APIs. - Implemented SDL2Renderer class, providing concrete methods for texture management, rendering primitives, and lifecycle management using SDL2. - Introduced texture creation, updating, and destruction methods, along with rendering functions to clear the screen and present frames. - Enhanced backend access for third-party library integration, ensuring flexibility in rendering operations.
This commit is contained in:
123
src/app/gfx/backend/irenderer.h
Normal file
123
src/app/gfx/backend/irenderer.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations prevent circular dependencies and speed up compilation.
|
||||
// Instead of including the full header, we just tell the compiler that these types exist.
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
class Bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief An abstract handle representing a texture.
|
||||
*
|
||||
* This typedef allows the underlying texture implementation (e.g., SDL_Texture*,
|
||||
* an OpenGL texture ID, etc.) to be hidden from the application logic.
|
||||
*/
|
||||
using TextureHandle = void*;
|
||||
|
||||
/**
|
||||
* @interface IRenderer
|
||||
* @brief Defines an abstract interface for all rendering operations.
|
||||
*
|
||||
* This interface decouples the application from any specific rendering API (like SDL2, SDL3, OpenGL, etc.).
|
||||
* It provides a contract for creating textures, managing their lifecycle, and performing
|
||||
* primitive drawing operations. The goal is to program against this interface, allowing the
|
||||
* concrete rendering backend to be swapped out with minimal changes to the application code.
|
||||
*/
|
||||
class IRenderer {
|
||||
public:
|
||||
virtual ~IRenderer() = default;
|
||||
|
||||
// --- Initialization and Lifecycle ---
|
||||
|
||||
/**
|
||||
* @brief Initializes the renderer with a given window.
|
||||
* @param window A pointer to the SDL_Window to render into.
|
||||
* @return True if initialization was successful, false otherwise.
|
||||
*/
|
||||
virtual bool Initialize(SDL_Window* window) = 0;
|
||||
|
||||
/**
|
||||
* @brief Shuts down the renderer and releases all associated resources.
|
||||
*/
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
// --- Texture Management ---
|
||||
|
||||
/**
|
||||
* @brief Creates a new, empty texture.
|
||||
* @param width The width of the texture in pixels.
|
||||
* @param height The height of the texture in pixels.
|
||||
* @return An abstract TextureHandle to the newly created texture, or nullptr on failure.
|
||||
*/
|
||||
virtual TextureHandle CreateTexture(int width, int height) = 0;
|
||||
|
||||
/**
|
||||
* @brief Updates a texture with the pixel data from a Bitmap.
|
||||
* @param texture The handle of the texture to update.
|
||||
* @param bitmap The Bitmap containing the new pixel data.
|
||||
*/
|
||||
virtual void UpdateTexture(TextureHandle texture, const Bitmap& bitmap) = 0;
|
||||
|
||||
/**
|
||||
* @brief Destroys a texture and frees its associated resources.
|
||||
* @param texture The handle of the texture to destroy.
|
||||
*/
|
||||
virtual void DestroyTexture(TextureHandle texture) = 0;
|
||||
|
||||
// --- Rendering Primitives ---
|
||||
|
||||
/**
|
||||
* @brief Clears the entire render target with the current draw color.
|
||||
*/
|
||||
virtual void Clear() = 0;
|
||||
|
||||
/**
|
||||
* @brief Presents the back buffer to the screen, making the rendered content visible.
|
||||
*/
|
||||
virtual void Present() = 0;
|
||||
|
||||
/**
|
||||
* @brief Copies a portion of a texture to the current render target.
|
||||
* @param texture The source texture handle.
|
||||
* @param srcrect A pointer to the source rectangle, or nullptr for the entire texture.
|
||||
* @param dstrect A pointer to the destination rectangle, or nullptr for the entire render target.
|
||||
*/
|
||||
virtual void RenderCopy(TextureHandle texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect) = 0;
|
||||
|
||||
/**
|
||||
* @brief Sets the render target for subsequent drawing operations.
|
||||
* @param texture The texture to set as the render target, or nullptr to set it back to the default (the window).
|
||||
*/
|
||||
virtual void SetRenderTarget(TextureHandle texture) = 0;
|
||||
|
||||
/**
|
||||
* @brief Sets the color used for drawing operations (e.g., Clear).
|
||||
* @param color The SDL_Color to use.
|
||||
*/
|
||||
virtual void SetDrawColor(SDL_Color color) = 0;
|
||||
|
||||
// --- Backend-specific Access ---
|
||||
|
||||
/**
|
||||
* @brief Provides an escape hatch to get the underlying, concrete renderer object.
|
||||
*
|
||||
* This is necessary for integrating with third-party libraries like ImGui that are tied
|
||||
* to a specific rendering backend (e.g., SDL_Renderer*, ID3D11Device*).
|
||||
*
|
||||
* @return A void pointer to the backend-specific renderer object. The caller is responsible
|
||||
* for casting it to the correct type.
|
||||
*/
|
||||
virtual void* GetBackendRenderer() = 0;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace yaze
|
||||
115
src/app/gfx/backend/sdl2_renderer.cc
Normal file
115
src/app/gfx/backend/sdl2_renderer.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "app/gfx/backend/sdl2_renderer.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
|
||||
SDL2Renderer::SDL2Renderer() = default;
|
||||
|
||||
SDL2Renderer::~SDL2Renderer() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the SDL2 renderer.
|
||||
* This function creates an accelerated SDL2 renderer and attaches it to the given window.
|
||||
*/
|
||||
bool SDL2Renderer::Initialize(SDL_Window* window) {
|
||||
// Create an SDL2 renderer with hardware acceleration.
|
||||
renderer_ = std::unique_ptr<SDL_Renderer, util::SDL_Deleter>(
|
||||
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED));
|
||||
|
||||
if (renderer_ == nullptr) {
|
||||
// Log an error if renderer creation fails.
|
||||
printf("SDL_CreateRenderer Error: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the blend mode to allow for transparency.
|
||||
SDL_SetRenderDrawBlendMode(renderer_.get(), SDL_BLENDMODE_BLEND);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shuts down the renderer.
|
||||
* The underlying SDL_Renderer is managed by a unique_ptr, so its destruction is handled automatically.
|
||||
*/
|
||||
void SDL2Renderer::Shutdown() {
|
||||
renderer_.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates an SDL_Texture.
|
||||
* The texture is created with streaming access, which is suitable for textures that are updated frequently.
|
||||
*/
|
||||
TextureHandle SDL2Renderer::CreateTexture(int width, int height) {
|
||||
// The TextureHandle is a void*, so we cast the SDL_Texture* to it.
|
||||
return static_cast<TextureHandle>(
|
||||
SDL_CreateTexture(renderer_.get(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates an SDL_Texture with data from a Bitmap.
|
||||
* This involves converting the bitmap's surface to the correct format and updating the texture.
|
||||
*/
|
||||
void SDL2Renderer::UpdateTexture(TextureHandle texture, const Bitmap& bitmap) {
|
||||
SDL_Surface* surface = bitmap.surface();
|
||||
if (!texture || !surface) return;
|
||||
|
||||
// Convert the bitmap's surface to RGBA8888 format for compatibility with the texture.
|
||||
auto converted_surface = std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(
|
||||
SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA8888, 0));
|
||||
|
||||
if (!converted_surface) return;
|
||||
|
||||
// Update the texture with the pixels from the converted surface.
|
||||
SDL_UpdateTexture(static_cast<SDL_Texture*>(texture), nullptr, converted_surface->pixels, converted_surface->pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys an SDL_Texture.
|
||||
*/
|
||||
void SDL2Renderer::DestroyTexture(TextureHandle texture) {
|
||||
if (texture) {
|
||||
SDL_DestroyTexture(static_cast<SDL_Texture*>(texture));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the screen with the current draw color.
|
||||
*/
|
||||
void SDL2Renderer::Clear() {
|
||||
SDL_RenderClear(renderer_.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Presents the rendered frame to the screen.
|
||||
*/
|
||||
void SDL2Renderer::Present() {
|
||||
SDL_RenderPresent(renderer_.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies a texture to the render target.
|
||||
*/
|
||||
void SDL2Renderer::RenderCopy(TextureHandle texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect) {
|
||||
SDL_RenderCopy(renderer_.get(), static_cast<SDL_Texture*>(texture), srcrect, dstrect);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the render target.
|
||||
*/
|
||||
void SDL2Renderer::SetRenderTarget(TextureHandle texture) {
|
||||
SDL_SetRenderTarget(renderer_.get(), static_cast<SDL_Texture*>(texture));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the draw color.
|
||||
*/
|
||||
void SDL2Renderer::SetDrawColor(SDL_Color color) {
|
||||
SDL_SetRenderDrawColor(renderer_.get(), color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace yaze
|
||||
51
src/app/gfx/backend/sdl2_renderer.h
Normal file
51
src/app/gfx/backend/sdl2_renderer.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "util/sdl_deleter.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @class SDL2Renderer
|
||||
* @brief A concrete implementation of the IRenderer interface using SDL2.
|
||||
*
|
||||
* This class encapsulates all rendering logic that is specific to the SDL2_render API.
|
||||
* It translates the abstract calls from the IRenderer interface into concrete SDL2 commands.
|
||||
* This is the first step in abstracting the renderer, allowing the rest of the application
|
||||
* to be independent of SDL2.
|
||||
*/
|
||||
class SDL2Renderer : public IRenderer {
|
||||
public:
|
||||
SDL2Renderer();
|
||||
~SDL2Renderer() override;
|
||||
|
||||
// --- Lifecycle and Initialization ---
|
||||
bool Initialize(SDL_Window* window) override;
|
||||
void Shutdown() override;
|
||||
|
||||
// --- Texture Management ---
|
||||
TextureHandle CreateTexture(int width, int height) override;
|
||||
void UpdateTexture(TextureHandle texture, const Bitmap& bitmap) override;
|
||||
void DestroyTexture(TextureHandle texture) override;
|
||||
|
||||
// --- Rendering Primitives ---
|
||||
void Clear() override;
|
||||
void Present() override;
|
||||
void RenderCopy(TextureHandle texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect) override;
|
||||
void SetRenderTarget(TextureHandle texture) override;
|
||||
void SetDrawColor(SDL_Color color) override;
|
||||
|
||||
/**
|
||||
* @brief Provides access to the underlying SDL_Renderer*.
|
||||
* @return A void pointer that can be safely cast to an SDL_Renderer*.
|
||||
*/
|
||||
void* GetBackendRenderer() override { return renderer_.get(); }
|
||||
|
||||
private:
|
||||
// The core SDL2 renderer object, managed by a unique_ptr with a custom deleter.
|
||||
std::unique_ptr<SDL_Renderer, util::SDL_Deleter> renderer_;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace yaze
|
||||
Reference in New Issue
Block a user