summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/render/drawsdl3accel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/modules/render/drawsdl3accel.cpp')
-rw-r--r--src/osd/modules/render/drawsdl3accel.cpp1165
1 files changed, 1165 insertions, 0 deletions
diff --git a/src/osd/modules/render/drawsdl3accel.cpp b/src/osd/modules/render/drawsdl3accel.cpp
new file mode 100644
index 00000000000..62f31f545fe
--- /dev/null
+++ b/src/osd/modules/render/drawsdl3accel.cpp
@@ -0,0 +1,1165 @@
+// license:BSD-3-Clause
+// copyright-holders: Couriersud, Olivier Galibert, R. Belmont
+//============================================================
+//
+// drawsdl3accel.cpp - SDL accelerated drawing using SDL's renderer API
+//
+// SDLMAME by Olivier Galibert and R. Belmont
+//
+// renderer_sdl2 by Couriersud
+//
+//============================================================
+
+#include "render_module.h"
+
+#include "modules/osdmodule.h"
+
+#if defined(OSD_SDL) && defined (SDLMAME_SDL3)
+
+// OSD headers
+#include "sdlopts.h"
+#include "window.h"
+
+// lib/util
+#include "options.h"
+
+// emu
+#include "emucore.h"
+#include "render.h"
+
+// standard SDL headers
+#include <SDL3/SDL.h>
+
+// standard C headers
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <iterator>
+#include <list>
+namespace osd {
+
+namespace {
+
+struct quad_setup_data
+{
+ quad_setup_data() = default;
+
+ void compute(const render_primitive &prim, const int prescale);
+
+ int32_t dudx = 0, dvdx = 0, dudy = 0, dvdy = 0;
+ int32_t startu = 0, startv = 0;
+ int32_t rotwidth = 0, rotheight = 0;
+};
+
+//============================================================
+// Textures
+//============================================================
+
+class renderer_sdl2;
+struct copy_info_t;
+
+/* texture_info holds information about a texture */
+class texture_info
+{
+public:
+ texture_info(renderer_sdl2 *renderer, const render_texinfo &texsource, const quad_setup_data &setup, const uint32_t flags);
+ ~texture_info();
+
+ void set_data(const render_texinfo &texsource, const uint32_t flags);
+ void render_quad(const render_primitive &prim, const int x, const int y);
+ bool matches(const render_primitive &prim, const quad_setup_data &setup);
+
+ copy_info_t const *compute_size_type();
+
+ void *m_pixels; // pixels for the texture
+ int m_pitch;
+
+ copy_info_t const *m_copyinfo;
+ quad_setup_data m_setup;
+
+ osd_ticks_t m_last_access;
+
+ int raw_width() const { return m_texinfo.width; }
+ int raw_height() const { return m_texinfo.height; }
+
+ const render_texinfo &texinfo() const { return m_texinfo; }
+ render_texinfo &texinfo() { return m_texinfo; }
+
+ HashT hash() const { return m_hash; }
+ uint32_t flags() const { return m_flags; }
+
+private:
+ bool is_pixels_owned() const;
+
+ void set_coloralphamode(SDL_Texture *texture_id, const render_color *color);
+
+ Uint32 m_sdl_access;
+ renderer_sdl2 * m_renderer;
+ render_texinfo m_texinfo; // copy of the texture info
+ HashT m_hash; // hash value for the texture (must be >= pointer size)
+ uint32_t m_flags; // rendering flags
+
+ SDL_Texture * m_texture_id;
+ bool m_is_rotated;
+
+ int m_format; // texture format
+ SDL_BlendMode m_sdl_blendmode;
+};
+
+// inline functions and macros
+#include "blit13.ipp"
+
+//============================================================
+// TEXCOPY FUNCS
+//============================================================
+
+enum SDL_TEXFORMAT_E
+{
+ SDL_TEXFORMAT_ARGB32 = 0,
+ SDL_TEXFORMAT_RGB32,
+ SDL_TEXFORMAT_RGB32_PALETTED,
+ SDL_TEXFORMAT_YUY16,
+ SDL_TEXFORMAT_YUY16_PALETTED,
+ SDL_TEXFORMAT_PALETTE16,
+ SDL_TEXFORMAT_RGB15,
+ SDL_TEXFORMAT_RGB15_PALETTED,
+ SDL_TEXFORMAT_PALETTE16A,
+ SDL_TEXFORMAT_PALETTE16_ARGB1555,
+ SDL_TEXFORMAT_RGB15_ARGB1555,
+ SDL_TEXFORMAT_RGB15_PALETTED_ARGB1555,
+ SDL_TEXFORMAT_LAST = SDL_TEXFORMAT_RGB15_PALETTED_ARGB1555
+};
+
+struct copy_info_t
+{
+ int src_fmt;
+ Uint32 dst_fmt;
+ const blit_base *blitter;
+ Uint32 bm_mask;
+ const char *srcname;
+ const char *dstname;
+ /* Statistics */
+ mutable uint64_t pixel_count;
+ mutable int64_t time;
+ mutable int samples;
+ mutable int perf;
+ /* list */
+ copy_info_t const *next;
+};
+
+/* renderer_sdl2 is the information about SDL for the current screen */
+class renderer_sdl2 : public osd_renderer
+{
+public:
+ renderer_sdl2(
+ osd_window &window,
+ copy_info_t const *const (&blit_info)[SDL_TEXFORMAT_LAST + 1]);
+
+ virtual ~renderer_sdl2()
+ {
+ destroy_all_textures();
+ SDL_DestroyRenderer(m_sdl_renderer);
+ m_sdl_renderer = nullptr;
+ }
+
+ virtual int create() override;
+ virtual int draw(const int update) override;
+ virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override;
+ virtual render_primitive_list *get_primitives() override;
+
+ int RendererSupportsFormat(Uint32 format, Uint32 access, const char *sformat);
+
+ SDL_Renderer *m_sdl_renderer;
+ copy_info_t const *const (&m_blit_info)[SDL_TEXFORMAT_LAST + 1];
+
+private:
+ void render_quad(texture_info *texture, const render_primitive &prim, const int x, const int y);
+
+ texture_info *texture_find(const render_primitive &prim, const quad_setup_data &setup);
+ texture_info *texture_update(const render_primitive &prim);
+
+ void destroy_all_textures();
+
+ int32_t m_blittimer;
+
+ std::list<texture_info> m_texlist; // list of active textures
+
+ float m_last_hofs;
+ float m_last_vofs;
+
+ int m_width;
+ int m_height;
+
+ osd_dim m_blit_dim;
+
+ struct
+ {
+ Uint32 format;
+ int status;
+ } fmt_support[30];
+
+ // Stats
+ int64_t m_last_blit_time;
+ int64_t m_last_blit_pixels;
+};
+
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#define STAT_PIXEL_THRESHOLD (150*150)
+
+enum
+{
+ TEXTURE_TYPE_NONE,
+ TEXTURE_TYPE_PLAIN,
+ TEXTURE_TYPE_SURFACE
+};
+
+
+//============================================================
+// Inline functions
+//============================================================
+
+static inline bool is_opaque(const float &a)
+{
+ return (a >= 1.0f);
+}
+
+static inline bool is_transparent(const float &a)
+{
+ return (a < 0.0001f);
+}
+
+//============================================================
+// CONSTRUCTOR & DESTRUCTOR
+//============================================================
+
+renderer_sdl2::renderer_sdl2(
+ osd_window &window,
+ copy_info_t const *const (&blit_info)[SDL_TEXFORMAT_LAST + 1])
+ : osd_renderer(window)
+ , m_sdl_renderer(nullptr)
+ , m_blit_info(blit_info)
+ , m_blittimer(0)
+ , m_last_hofs(0)
+ , m_last_vofs(0)
+ , m_width(0)
+ , m_height(0)
+ , m_blit_dim(0, 0)
+ , m_last_blit_time(0)
+ , m_last_blit_pixels(0)
+{
+ for (int i = 0; i < 30; i++)
+ {
+ fmt_support[i].format = 0;
+ fmt_support[i].status = 0;
+ }
+}
+
+//============================================================
+// INLINES
+//============================================================
+
+
+static inline float round_nearest(float f)
+{
+ return floor(f + 0.5f);
+}
+
+static inline HashT texture_compute_hash(const render_texinfo &texture, const uint32_t flags)
+{
+ return (HashT)texture.base ^ (flags & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK));
+}
+
+static inline SDL_BlendMode map_blendmode(const int blendmode)
+{
+ switch (blendmode)
+ {
+ case BLENDMODE_NONE:
+ return SDL_BLENDMODE_NONE;
+ case BLENDMODE_ALPHA:
+ return SDL_BLENDMODE_BLEND;
+ case BLENDMODE_RGB_MULTIPLY:
+ return SDL_BLENDMODE_MOD;
+ case BLENDMODE_ADD:
+ return SDL_BLENDMODE_ADD;
+ default:
+ osd_printf_warning("Unknown Blendmode %d", blendmode);
+ }
+ return SDL_BLENDMODE_NONE;
+}
+
+void texture_info::set_coloralphamode(SDL_Texture *texture_id, const render_color *color)
+{
+ uint32_t sr = (uint32_t)(255.0f * color->r);
+ uint32_t sg = (uint32_t)(255.0f * color->g);
+ uint32_t sb = (uint32_t)(255.0f * color->b);
+ uint32_t sa = (uint32_t)(255.0f * color->a);
+
+
+ if (color->r >= 1.0f && color->g >= 1.0f && color->b >= 1.0f && is_opaque(color->a))
+ {
+ SDL_SetTextureColorMod(texture_id, 0xFF, 0xFF, 0xFF);
+ SDL_SetTextureAlphaMod(texture_id, 0xFF);
+ }
+ /* coloring-only case */
+ else if (is_opaque(color->a))
+ {
+ SDL_SetTextureColorMod(texture_id, sr, sg, sb);
+ SDL_SetTextureAlphaMod(texture_id, 0xFF);
+ }
+ /* alpha and/or coloring case */
+ else if (!is_transparent(color->a))
+ {
+ SDL_SetTextureColorMod(texture_id, sr, sg, sb);
+ SDL_SetTextureAlphaMod(texture_id, sa);
+ }
+ else
+ {
+ SDL_SetTextureColorMod(texture_id, 0xFF, 0xFF, 0xFF);
+ SDL_SetTextureAlphaMod(texture_id, 0x00);
+ }
+}
+
+void texture_info::render_quad(const render_primitive &prim, const int x, const int y)
+{
+ SDL_FRect target_rect;
+
+ target_rect.x = x;
+ target_rect.y = y;
+ target_rect.w = round_nearest(prim.bounds.x1) - round_nearest(prim.bounds.x0);
+ target_rect.h = round_nearest(prim.bounds.y1) - round_nearest(prim.bounds.y0);
+
+ SDL_SetTextureBlendMode(m_texture_id, m_sdl_blendmode);
+ set_coloralphamode(m_texture_id, &prim.color);
+ //printf("%d %d %d %d\n", target_rect.x, target_rect.y, target_rect.w, target_rect.h);
+ // Arghhh .. Just another bug. SDL_RenderTexture has severe issues with scaling ...
+ SDL_RenderTexture(m_renderer->m_sdl_renderer, m_texture_id, nullptr, &target_rect);
+ //SDL_RenderTextureRotated(m_renderer->m_sdl_renderer, m_texture_id, nullptr, &target_rect, 0, nullptr, SDL_FLIP_NONE);
+ //SDL_RenderTextureRotated(m_renderer->m_sdl_renderer, m_texture_id, nullptr, nullptr, 0, nullptr, SDL_FLIP_NONE);
+}
+
+void renderer_sdl2::render_quad(texture_info *texture, const render_primitive &prim, const int x, const int y)
+{
+ SDL_FRect target_rect;
+
+ target_rect.x = x;
+ target_rect.y = y;
+ target_rect.w = round_nearest(prim.bounds.x1 - prim.bounds.x0);
+ target_rect.h = round_nearest(prim.bounds.y1 - prim.bounds.y0);
+
+ if (texture)
+ {
+ copy_info_t const *copyinfo = texture->m_copyinfo;
+ copyinfo->time -= osd_ticks();
+ texture->render_quad(prim, x, y);
+ copyinfo->time += osd_ticks();
+
+ copyinfo->pixel_count += std::max(STAT_PIXEL_THRESHOLD , (texture->raw_width() * texture->raw_height()));
+ if (m_last_blit_pixels)
+ {
+ copyinfo->time += (m_last_blit_time * (int64_t) (texture->raw_width() * texture->raw_height())) / (int64_t) m_last_blit_pixels;
+ }
+ copyinfo->samples++;
+ copyinfo->perf = (texture->m_copyinfo->pixel_count * (osd_ticks_per_second()/1000)) / std::max<int64_t>(texture->m_copyinfo->time, 1);
+ }
+ else
+ {
+ uint32_t sr = (uint32_t)(255.0f * prim.color.r);
+ uint32_t sg = (uint32_t)(255.0f * prim.color.g);
+ uint32_t sb = (uint32_t)(255.0f * prim.color.b);
+ uint32_t sa = (uint32_t)(255.0f * prim.color.a);
+
+ SDL_SetRenderDrawBlendMode(m_sdl_renderer, map_blendmode(PRIMFLAG_GET_BLENDMODE(prim.flags)));
+ SDL_SetRenderDrawColor(m_sdl_renderer, sr, sg, sb, sa);
+ SDL_RenderFillRect(m_sdl_renderer, &target_rect);
+ }
+}
+
+int renderer_sdl2::RendererSupportsFormat(Uint32 format, Uint32 access, const char *sformat)
+{
+ int i;
+ for (i = 0; fmt_support[i].format != 0; i++)
+ {
+ if (format == fmt_support[i].format)
+ {
+ return fmt_support[i].status;
+ }
+ }
+ /* not tested yet */
+ fmt_support[i].format = format;
+ fmt_support[i + 1].format = 0;
+ SDL_Texture *texid = SDL_CreateTexture(m_sdl_renderer, SDL_PixelFormat(format), SDL_TextureAccess(access), 16, 16);
+ if (texid)
+ {
+ fmt_support[i].status = 1;
+ SDL_DestroyTexture(texid);
+ return 1;
+ }
+ osd_printf_verbose("Pixelformat <%s> error %s \n", sformat, SDL_GetError());
+ osd_printf_verbose("Pixelformat <%s> not supported\n", sformat);
+ fmt_support[i].status = 0;
+ return 0;
+}
+
+
+//============================================================
+// sdl_info::create
+//============================================================
+int renderer_sdl2::create()
+{
+ osd_printf_verbose("Enter renderer_sdl2::create\n");
+
+ // create renderer
+ m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), nullptr);
+
+ if (!m_sdl_renderer)
+ {
+ fatalerror("Error on creating renderer: %s\n", SDL_GetError());
+ }
+
+ if (video_config.waitvsync)
+ {
+ SDL_SetRenderVSync(m_sdl_renderer, SDL_RENDERER_VSYNC_ADAPTIVE);
+ }
+
+ /* Enable bilinear filtering in case it is supported.
+ * This applies to all texture operations. However, artwort is pre-scaled
+ * and thus shouldn't be affected.
+ */
+#if SDL_VERSION_ATLEAST(3, 3, 2)
+ if (video_config.filter)
+ {
+ SDL_SetDefaultTextureScaleMode(m_sdl_renderer, SDL_SCALEMODE_LINEAR);
+ }
+ else
+ {
+ SDL_SetDefaultTextureScaleMode(m_sdl_renderer, SDL_SCALEMODE_NEAREST);
+ }
+#endif
+ m_blittimer = 3;
+
+ const auto props = SDL_GetRendererProperties(m_sdl_renderer);
+ osd_printf_verbose("SDL renderer using driver %s\n", SDL_GetStringProperty(props, SDL_PROP_RENDERER_NAME_STRING, "Unknown"));
+
+ osd_printf_verbose("Leave renderer_sdl2::create\n");
+ return 0;
+}
+
+
+//============================================================
+// drawsdl_xy_to_render_target
+//============================================================
+
+int renderer_sdl2::xy_to_render_target(int x, int y, int *xt, int *yt)
+{
+ *xt = x - m_last_hofs;
+ *yt = y - m_last_vofs;
+ if (*xt<0 || *xt >= m_blit_dim.width())
+ return 0;
+ if (*yt<0 || *yt >= m_blit_dim.height())
+ return 0;
+ return 1;
+}
+
+//============================================================
+// drawsdl_destroy_all_textures
+//============================================================
+
+void renderer_sdl2::destroy_all_textures()
+{
+ if (window().m_primlist)
+ {
+ window().m_primlist->acquire_lock();
+ m_texlist.clear();
+ window().m_primlist->release_lock();
+ }
+ else
+ m_texlist.clear();
+}
+
+//============================================================
+// sdl_info::draw
+//============================================================
+
+int renderer_sdl2::draw(int update)
+{
+ texture_info *texture=nullptr;
+ float vofs, hofs;
+ int blit_pixels = 0;
+
+ osd_dim wdim = window().get_size();
+
+ if (has_flags(FI_CHANGED) || (wdim.width() != m_width) || (wdim.height() != m_height))
+ {
+ destroy_all_textures();
+ m_width = wdim.width();
+ m_height = wdim.height();
+ SDL_SetRenderViewport(m_sdl_renderer, nullptr);
+ m_blittimer = 3;
+ clear_flags(FI_CHANGED);
+ }
+
+ //SDL_SelectRenderer(window().sdl_window);
+
+ if (m_blittimer > 0)
+ {
+ /* SDL Underlays need alpha = 0 ! */
+ SDL_SetRenderDrawBlendMode(m_sdl_renderer, SDL_BLENDMODE_NONE);
+ //SDL_SetRenderDrawColor(0,0,0,255);
+ SDL_SetRenderDrawColor(m_sdl_renderer, 0,0,0,0);
+ SDL_RenderFillRect(m_sdl_renderer, nullptr);
+ m_blittimer--;
+ }
+
+ // compute centering parameters
+ vofs = hofs = 0.0f;
+
+ if (video_config.centerv || video_config.centerh)
+ {
+ int ch, cw;
+
+ ch = wdim.height();
+ cw = wdim.width();
+
+ if (video_config.centerv)
+ {
+ vofs = (ch - m_blit_dim.height()) / 2.0f;
+ }
+ if (video_config.centerh)
+ {
+ hofs = (cw - m_blit_dim.width()) / 2.0f;
+ }
+ }
+
+ m_last_hofs = hofs;
+ m_last_vofs = vofs;
+
+ window().m_primlist->acquire_lock();
+
+ // now draw
+ for (render_primitive &prim : *window().m_primlist)
+ {
+ Uint8 sr, sg, sb, sa;
+
+ switch (prim.type)
+ {
+ case render_primitive::LINE:
+ sr = (int)(255.0f * prim.color.r);
+ sg = (int)(255.0f * prim.color.g);
+ sb = (int)(255.0f * prim.color.b);
+ sa = (int)(255.0f * prim.color.a);
+
+ SDL_SetRenderDrawBlendMode(m_sdl_renderer, map_blendmode(PRIMFLAG_GET_BLENDMODE(prim.flags)));
+ SDL_SetRenderDrawColor(m_sdl_renderer, sr, sg, sb, sa);
+ SDL_RenderLine(m_sdl_renderer, prim.bounds.x0 + hofs, prim.bounds.y0 + vofs,
+ prim.bounds.x1 + hofs, prim.bounds.y1 + vofs);
+ break;
+ case render_primitive::QUAD:
+ texture = texture_update(prim);
+ if (texture)
+ blit_pixels += (texture->raw_height() * texture->raw_width());
+ render_quad(texture, prim,
+ round_nearest(hofs + prim.bounds.x0),
+ round_nearest(vofs + prim.bounds.y0));
+ break;
+ default:
+ throw emu_fatalerror("Unexpected render_primitive type\n");
+ }
+ }
+
+ window().m_primlist->release_lock();
+
+ m_last_blit_pixels = blit_pixels;
+ m_last_blit_time = -osd_ticks();
+ SDL_RenderPresent(m_sdl_renderer);
+ m_last_blit_time += osd_ticks();
+
+ return 0;
+}
+
+
+//============================================================
+// texture handling
+//============================================================
+
+//============================================================
+// texture_compute_size and type
+//============================================================
+
+copy_info_t const *texture_info::compute_size_type()
+{
+ copy_info_t const *result = nullptr;
+ int maxperf = 0;
+
+ for (copy_info_t const *bi = m_renderer->m_blit_info[m_format]; bi != nullptr; bi = bi->next)
+ {
+ if ((m_is_rotated == bi->blitter->m_is_rot) && (m_sdl_blendmode == bi->bm_mask))
+ {
+ if (m_renderer->RendererSupportsFormat(bi->dst_fmt, m_sdl_access, bi->dstname))
+ {
+ int const perf = bi->perf;
+ if (perf == 0)
+ {
+ return bi;
+ }
+ else if (perf > ((maxperf * 102) / 100))
+ {
+ result = bi;
+ maxperf = perf;
+ }
+ }
+ }
+ }
+
+ if (result)
+ return result;
+
+ // try last resort handlers
+ for (copy_info_t const *bi = m_renderer->m_blit_info[m_format]; bi != nullptr; bi = bi->next)
+ {
+ if ((m_is_rotated == bi->blitter->m_is_rot) && (m_sdl_blendmode == bi->bm_mask))
+ if (m_renderer->RendererSupportsFormat(bi->dst_fmt, m_sdl_access, bi->dstname))
+ return bi;
+ }
+ //FIXME: crash implement a -do nothing handler
+ return nullptr;
+}
+
+bool texture_info::is_pixels_owned() const
+{
+ // do we own / allocated it ?
+ return (m_sdl_access == SDL_TEXTUREACCESS_STATIC) && !m_copyinfo->blitter->m_is_passthrough;
+}
+
+//============================================================
+// texture_info::matches
+//============================================================
+
+bool texture_info::matches(const render_primitive &prim, const quad_setup_data &setup)
+{
+ return texinfo().base == prim.texture.base &&
+ texinfo().width == prim.texture.width &&
+ texinfo().height == prim.texture.height &&
+ texinfo().rowpixels == prim.texture.rowpixels &&
+ m_setup.dudx == setup.dudx &&
+ m_setup.dvdx == setup.dvdx &&
+ m_setup.dudy == setup.dudy &&
+ m_setup.dvdy == setup.dvdy &&
+ m_setup.startu == setup.startu &&
+ m_setup.startv == setup.startv &&
+ ((flags() ^ prim.flags) & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) == 0;
+}
+
+//============================================================
+// texture_create
+//============================================================
+
+texture_info::texture_info(renderer_sdl2 *renderer, const render_texinfo &texsource, const quad_setup_data &setup, uint32_t flags)
+{
+ // fill in the core data
+ m_renderer = renderer;
+ m_hash = texture_compute_hash(texsource, flags);
+ m_flags = flags;
+ m_texinfo = texsource;
+ m_texinfo.seqid = -1; // force set data
+ m_is_rotated = false;
+ m_setup = setup;
+ m_sdl_blendmode = map_blendmode(PRIMFLAG_GET_BLENDMODE(flags));
+ m_pitch = 0;
+
+ switch (PRIMFLAG_GET_TEXFORMAT(flags))
+ {
+ case TEXFORMAT_ARGB32:
+ m_format = SDL_TEXFORMAT_ARGB32;
+ break;
+ case TEXFORMAT_RGB32:
+ m_format = texsource.palette ? SDL_TEXFORMAT_RGB32_PALETTED : SDL_TEXFORMAT_RGB32;
+ break;
+ case TEXFORMAT_PALETTE16:
+ m_format = SDL_TEXFORMAT_PALETTE16;
+ break;
+ case TEXFORMAT_YUY16:
+ m_format = texsource.palette ? SDL_TEXFORMAT_YUY16_PALETTED : SDL_TEXFORMAT_YUY16;
+ break;
+
+ default:
+ osd_printf_error("Unknown textureformat %d\n", PRIMFLAG_GET_TEXFORMAT(flags));
+ }
+
+ if (setup.rotwidth != m_texinfo.width || setup.rotheight != m_texinfo.height
+ || setup.dudx < 0 || setup.dvdy < 0 || (PRIMFLAG_GET_TEXORIENT(flags) != 0))
+ m_is_rotated = true;
+ else
+ m_is_rotated = false;
+
+ m_sdl_access = SDL_TEXTUREACCESS_STREAMING;
+
+ // Watch out for 0x0 textures ...
+ if (!m_setup.rotwidth || !m_setup.rotheight)
+ osd_printf_warning("Trying to create texture with zero dim\n");
+
+ // set copy_info
+ m_copyinfo = compute_size_type();
+
+ m_texture_id = SDL_CreateTexture(m_renderer->m_sdl_renderer, SDL_PixelFormat(m_copyinfo->dst_fmt), SDL_TextureAccess(m_sdl_access),
+ m_setup.rotwidth, m_setup.rotheight);
+
+ if (!m_texture_id)
+ osd_printf_error("Error creating texture: %d x %d, pixelformat %s error: %s\n", m_setup.rotwidth, m_setup.rotheight,
+ m_copyinfo->dstname, SDL_GetError());
+
+#if !SDL_VERSION_ATLEAST(3, 3, 2)
+ if (video_config.filter)
+ {
+ SDL_SetTextureScaleMode(m_texture_id, SDL_SCALEMODE_LINEAR);
+ }
+ else
+ {
+ SDL_SetTextureScaleMode(m_texture_id, SDL_SCALEMODE_NEAREST);
+ }
+#endif
+
+ if (m_sdl_access == SDL_TEXTUREACCESS_STATIC)
+ {
+ if (m_copyinfo->blitter->m_is_passthrough)
+ m_pixels = nullptr;
+ else
+ m_pixels = malloc(m_setup.rotwidth * m_setup.rotheight * m_copyinfo->blitter->m_dest_bpp);
+ }
+ m_last_access = osd_ticks();
+}
+
+texture_info::~texture_info()
+{
+ if (is_pixels_owned() && m_pixels)
+ free(m_pixels);
+ SDL_DestroyTexture(m_texture_id);
+}
+
+//============================================================
+// texture_set_data
+//============================================================
+
+void texture_info::set_data(const render_texinfo &texsource, const uint32_t flags)
+{
+ m_copyinfo->time -= osd_ticks();
+ if (m_sdl_access == SDL_TEXTUREACCESS_STATIC)
+ {
+ if (m_copyinfo->blitter->m_is_passthrough)
+ {
+ m_pixels = texsource.base;
+ m_pitch = m_texinfo.rowpixels * m_copyinfo->blitter->m_dest_bpp;
+ }
+ else
+ {
+ m_pitch = m_setup.rotwidth * m_copyinfo->blitter->m_dest_bpp;
+ m_copyinfo->blitter->texop(this, &texsource);
+ }
+ SDL_UpdateTexture(m_texture_id, nullptr, m_pixels, m_pitch);
+ }
+ else
+ {
+ SDL_LockTexture(m_texture_id, nullptr, (void **)&m_pixels, &m_pitch);
+ if ( m_copyinfo->blitter->m_is_passthrough )
+ {
+ const uint8_t *src = (uint8_t *)texsource.base;
+ uint8_t *dst = (uint8_t *)m_pixels;
+ int spitch = texsource.rowpixels * m_copyinfo->blitter->m_dest_bpp;
+ int num = texsource.width * m_copyinfo->blitter->m_dest_bpp;
+ int h = texsource.height;
+ while (h--) {
+ memcpy(dst, src, num);
+ src += spitch;
+ dst += m_pitch;
+ }
+ }
+ else
+ m_copyinfo->blitter->texop(this, &texsource);
+ SDL_UnlockTexture(m_texture_id);
+ }
+ m_copyinfo->time += osd_ticks();
+}
+
+//============================================================
+// compute rotation setup
+//============================================================
+
+inline float signf(const float a)
+{
+ return (0.0f < a) - (a < 0.0f);
+}
+
+void quad_setup_data::compute(const render_primitive &prim, const int prescale)
+{
+ const render_quad_texuv *texcoords = &prim.texcoords;
+ int texwidth = prim.texture.width;
+ int texheight = prim.texture.height;
+ float fdudx, fdvdx, fdudy, fdvdy;
+ float width, height;
+ float fscale;
+ /* determine U/V deltas */
+ if ((PRIMFLAG_GET_SCREENTEX(prim.flags)))
+ fscale = (float) prescale;
+ else
+ fscale = 1.0f;
+
+ fdudx = (texcoords->tr.u - texcoords->tl.u); // a a11
+ fdvdx = (texcoords->tr.v - texcoords->tl.v); // c a21
+ fdudy = (texcoords->bl.u - texcoords->tl.u); // b a12
+ fdvdy = (texcoords->bl.v - texcoords->tl.v); // d a22
+
+ width = fabsf(( fdudx * (float) (texwidth) + fdvdx * (float) (texheight)) ) * fscale;
+ height = fabsf((fdudy * (float) (texwidth) + fdvdy * (float) (texheight)) ) * fscale;
+
+ fdudx = signf(fdudx) / fscale;
+ fdvdy = signf(fdvdy) / fscale;
+ fdvdx = signf(fdvdx) / fscale;
+ fdudy = signf(fdudy) / fscale;
+
+#if 0
+ printf("tl.u %f tl.v %f\n", texcoords->tl.u, texcoords->tl.v);
+ printf("tr.u %f tr.v %f\n", texcoords->tr.u, texcoords->tr.v);
+ printf("bl.u %f bl.v %f\n", texcoords->bl.u, texcoords->bl.v);
+ printf("br.u %f br.v %f\n", texcoords->br.u, texcoords->br.v);
+ /* compute start and delta U,V coordinates now */
+#endif
+
+ dudx = round_nearest(65536.0f * fdudx);
+ dvdx = round_nearest(65536.0f * fdvdx);
+ dudy = round_nearest(65536.0f * fdudy);
+ dvdy = round_nearest(65536.0f * fdvdy);
+ startu = round_nearest(65536.0f * (float) texwidth * texcoords->tl.u);
+ startv = round_nearest(65536.0f * (float) texheight * texcoords->tl.v);
+
+ /* clamp to integers */
+
+ rotwidth = round_nearest(width);
+ rotheight = round_nearest(height);
+
+ //printf("%d %d rot %d %d\n", texwidth, texheight, rotwidth, rotheight);
+
+ startu += (dudx + dudy) / 2;
+ startv += (dvdx + dvdy) / 2;
+
+}
+
+//============================================================
+// texture_find
+//============================================================
+
+texture_info *renderer_sdl2::texture_find(const render_primitive &prim, const quad_setup_data &setup)
+{
+ const HashT texhash = texture_compute_hash(prim.texture, prim.flags);
+ const osd_ticks_t now = osd_ticks();
+
+ // find a match
+ for (auto texture = m_texlist.begin(); texture != m_texlist.end(); )
+ {
+ if ((texture->hash() == texhash) && texture->matches(prim, setup))
+ {
+ // would we choose another blitter based on performance?
+ if ((texture->m_copyinfo->samples & 0x7f) == 0x7f)
+ {
+ if (texture->m_copyinfo != texture->compute_size_type())
+ return nullptr;
+ }
+ texture->m_last_access = now;
+ return &*texture;
+ }
+ else
+ {
+ // free resources not needed any longer?
+ if ((now - texture->m_last_access) > osd_ticks_per_second())
+ texture = m_texlist.erase(texture);
+ else
+ ++texture;
+ }
+ }
+
+ // nothing found
+ return nullptr;
+}
+
+//============================================================
+// texture_update
+//============================================================
+
+texture_info * renderer_sdl2::texture_update(const render_primitive &prim)
+{
+ quad_setup_data setup;
+ texture_info *texture;
+
+ setup.compute(prim, window().prescale());
+
+ texture = texture_find(prim, setup);
+
+ // if we didn't find one, create a new texture
+ if (!texture && prim.texture.base)
+ {
+ // add us to the texture list
+ texture = &m_texlist.emplace_front(this, prim.texture, setup, prim.flags);
+ }
+
+ if (texture)
+ {
+ if (prim.texture.base && (texture->texinfo().seqid != prim.texture.seqid))
+ {
+ texture->texinfo().seqid = prim.texture.seqid;
+ // if we found it, but with a different seqid, copy the data
+ texture->set_data(prim.texture, prim.flags);
+ }
+
+ }
+ return texture;
+}
+
+render_primitive_list *renderer_sdl2::get_primitives()
+{
+ osd_dim nd = window().get_size();
+ if (nd != m_blit_dim)
+ {
+ m_blit_dim = nd;
+ notify_changed();
+ }
+ window().target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), window().pixel_aspect());
+ return &window().target()->get_primitives();
+}
+
+
+class video_sdl3_accel : public osd_module, public render_module
+{
+public:
+ video_sdl3_accel()
+ : osd_module(OSD_RENDERER_PROVIDER, "accel")
+ , m_blit_info_initialized(false)
+ , m_gllib_loaded(false)
+ {
+ std::fill(std::begin(m_blit_info), std::end(m_blit_info), nullptr);
+ }
+ ~video_sdl3_accel()
+ {
+ free_copy_info();
+ }
+
+ virtual int init(osd_interface &osd, osd_options const &options) override;
+ virtual void exit() override { free_copy_info(); }
+
+ virtual std::unique_ptr<osd_renderer> create(osd_window &window) override;
+
+protected:
+ virtual unsigned flags() const override { return FLAG_INTERACTIVE | FLAG_SDL_NEEDS_OPENGL; }
+
+private:
+ static inline constexpr Uint32 BM_ALL = UINT32_MAX; // SDL_BLENDMODE_MASK | SDL_BLENDMODE_BLEND | SDL_BLENDMODE_ADD | SDL_BLENDMODE_MOD
+
+ void expand_copy_info();
+ void free_copy_info();
+
+ static void add_list(copy_info_t const *&head, copy_info_t const &element, Uint32 bm);
+
+ copy_info_t const *m_blit_info[SDL_TEXFORMAT_LAST + 1];
+ bool m_blit_info_initialized;
+ bool m_gllib_loaded;
+
+ static copy_info_t const s_blit_info_default[];
+};
+
+int video_sdl3_accel::init(osd_interface &osd, osd_options const &options)
+{
+ osd_printf_verbose("Using SDL native texturing driver (SDL 3.2+)\n");
+
+ // Load the GL library now - else MT will fail
+ char const *libname = nullptr;
+#if USE_OPENGL
+ libname = dynamic_cast<sdl_options const &>(options).gl_lib();
+ if (libname && (!*libname || !std::strcmp(libname, OSDOPTVAL_AUTO)))
+ libname = nullptr;
+#endif
+
+ if (!m_gllib_loaded)
+ {
+ // No fatalerror here since not all video drivers support GL!
+ if (SDL_GL_LoadLibrary(libname) != 0)
+ {
+ osd_printf_error("Unable to load OpenGL shared library: %s\n", libname ? libname : "<default>");
+ m_gllib_loaded = true;
+ }
+ else
+ {
+ osd_printf_verbose("Loaded OpenGL shared library: %s\n", libname ? libname : "<default>");
+ }
+ }
+
+ return 0;
+}
+
+std::unique_ptr<osd_renderer> video_sdl3_accel::create(osd_window &window)
+{
+ if (!m_blit_info_initialized)
+ {
+ // On macOS, calling this from drawsdl2_init will prohibit fullscreen toggling.
+ // It is than not possible to toggle from fullscreen to window mode.
+ expand_copy_info();
+ m_blit_info_initialized = true;
+ }
+
+ return std::make_unique<renderer_sdl2>(window, m_blit_info);
+}
+
+void video_sdl3_accel::expand_copy_info()
+{
+ for (const copy_info_t *bi = s_blit_info_default; bi->src_fmt != -1; bi++)
+ {
+ if (bi->bm_mask == BM_ALL)
+ {
+ add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_NONE);
+ add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_ADD);
+ add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_MOD);
+ add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_BLEND);
+ }
+ else
+ {
+ add_list(m_blit_info[bi->src_fmt], *bi, bi->bm_mask);
+ }
+ }
+}
+
+void video_sdl3_accel::free_copy_info()
+{
+ if (m_blit_info_initialized)
+ {
+ for (int i = 0; i <= SDL_TEXFORMAT_LAST; i++)
+ {
+ for (copy_info_t const *bi = m_blit_info[i]; bi != nullptr; )
+ {
+ if (bi->pixel_count)
+ {
+ osd_printf_verbose(
+ "%s -> %s %s blendmode 0x%02x, %d samples: %d KPixel/sec\n",
+ bi->srcname,
+ bi->dstname,
+ bi->blitter->m_is_rot ? "rot" : "norot",
+ bi->bm_mask,
+ bi->samples,
+ bi->perf);
+ }
+ delete std::exchange(bi, bi->next);
+ }
+ m_blit_info[i] = nullptr;
+ }
+ m_blit_info_initialized = false;
+ }
+}
+
+void video_sdl3_accel::add_list(copy_info_t const *&head, copy_info_t const &element, Uint32 bm)
+{
+ copy_info_t *const newci = new copy_info_t(element);
+
+ newci->bm_mask = bm;
+ newci->next = head;
+ head = newci;
+}
+
+
+//============================================================
+// STATIC VARIABLES
+//============================================================
+
+#define ENTRY(a,b,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, 0}
+#define ENTRY_BM(a,b,f,bm) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, bm, #a, #b, 0, 0, 0, 0}
+#define ENTRY_LR(a,b,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, -1}
+
+copy_info_t const video_sdl3_accel::s_blit_info_default[] =
+{
+ /* no rotation */
+ ENTRY(ARGB32, ARGB8888, argb32_argb32),
+ ENTRY_LR(ARGB32, XRGB8888, argb32_rgb32),
+ /* Entry primarily for directfb */
+ ENTRY_BM(ARGB32, XRGB8888, argb32_rgb32, SDL_BLENDMODE_ADD),
+ ENTRY_BM(ARGB32, XRGB8888, argb32_rgb32, SDL_BLENDMODE_MOD),
+ ENTRY_BM(ARGB32, XRGB8888, argb32_rgb32, SDL_BLENDMODE_NONE),
+
+ ENTRY(RGB32, ARGB8888, rgb32_argb32),
+ ENTRY(RGB32, XRGB8888, rgb32_rgb32),
+
+ ENTRY(RGB32_PALETTED, ARGB8888, rgb32pal_argb32),
+ ENTRY(RGB32_PALETTED, XRGB8888, rgb32pal_argb32),
+
+ ENTRY(YUY16, UYVY, yuv16_uyvy),
+ ENTRY(YUY16, YUY2, yuv16_yuy2),
+ ENTRY(YUY16, YVYU, yuv16_yvyu),
+ ENTRY(YUY16, ARGB8888, yuv16_argb32),
+ ENTRY(YUY16, XRGB8888, yuv16_argb32),
+
+ ENTRY(YUY16_PALETTED, UYVY, yuv16pal_uyvy),
+ ENTRY(YUY16_PALETTED, YUY2, yuv16pal_yuy2),
+ ENTRY(YUY16_PALETTED, YVYU, yuv16pal_yvyu),
+ ENTRY(YUY16_PALETTED, ARGB8888, yuv16pal_argb32),
+ ENTRY(YUY16_PALETTED, XRGB8888, yuv16pal_argb32),
+
+ ENTRY(PALETTE16, ARGB8888, pal16_argb32),
+ ENTRY(PALETTE16, XRGB8888, pal16_argb32),
+
+ ENTRY(RGB15, XRGB1555, rgb15_rgb555),
+ ENTRY(RGB15, ARGB1555, rgb15_argb1555),
+ ENTRY(RGB15, ARGB8888, rgb15_argb32),
+ ENTRY(RGB15, XRGB8888, rgb15_argb32),
+
+ ENTRY(RGB15_PALETTED, ARGB8888, rgb15pal_argb32),
+ ENTRY(RGB15_PALETTED, XRGB8888, rgb15pal_argb32),
+
+ ENTRY(PALETTE16A, ARGB8888, pal16a_argb32),
+ ENTRY(PALETTE16A, XRGB8888, pal16a_rgb32),
+
+ /* rotation */
+ ENTRY(ARGB32, ARGB8888, rot_argb32_argb32),
+ ENTRY_LR(ARGB32, XRGB8888, rot_argb32_rgb32),
+ /* Entry primarily for directfb */
+ ENTRY_BM(ARGB32, XRGB8888, rot_argb32_rgb32, SDL_BLENDMODE_ADD),
+ ENTRY_BM(ARGB32, XRGB8888, rot_argb32_rgb32, SDL_BLENDMODE_MOD),
+ ENTRY_BM(ARGB32, XRGB8888, rot_argb32_rgb32, SDL_BLENDMODE_NONE),
+
+ ENTRY(RGB32, ARGB8888, rot_rgb32_argb32),
+ ENTRY(RGB32, XRGB8888, rot_argb32_argb32),
+
+ ENTRY(RGB32_PALETTED, ARGB8888, rot_rgb32pal_argb32),
+ ENTRY(RGB32_PALETTED, XRGB8888, rot_rgb32pal_argb32),
+
+ ENTRY(YUY16, ARGB8888, rot_yuv16_argb32rot),
+ ENTRY(YUY16, XRGB8888, rot_yuv16_argb32rot),
+
+ ENTRY(YUY16_PALETTED, ARGB8888, rot_yuv16pal_argb32rot),
+ ENTRY(YUY16_PALETTED, XRGB8888, rot_yuv16pal_argb32rot),
+
+ ENTRY(PALETTE16, ARGB8888, rot_pal16_argb32),
+ ENTRY(PALETTE16, XRGB8888, rot_pal16_argb32),
+
+ ENTRY(RGB15, XRGB1555, rot_rgb15_argb1555),
+ ENTRY(RGB15, ARGB1555, rot_rgb15_argb1555),
+ ENTRY(RGB15, ARGB8888, rot_rgb15_argb32),
+ ENTRY(RGB15, XRGB8888, rot_rgb15_argb32),
+
+ ENTRY(RGB15_PALETTED, ARGB8888, rot_rgb15pal_argb32),
+ ENTRY(RGB15_PALETTED, XRGB8888, rot_rgb15pal_argb32),
+
+ ENTRY(PALETTE16A, ARGB8888, rot_pal16a_argb32),
+ ENTRY(PALETTE16A, XRGB8888, rot_pal16a_rgb32),
+
+ { -1 },
+};
+
+} // anonymous namespace
+
+} // namespace osd
+
+
+#else // defined(OSD_SDL) && defined (SDLMAME_SDL3)
+
+namespace osd { namespace { MODULE_NOT_SUPPORTED(video_sdl3_accel, OSD_RENDERER_PROVIDER, "accel") } }
+
+#endif // defined(OSD_SDL) && defined (SDLMAME_SDL3)
+
+MODULE_DEFINITION(RENDERER_SDL3ACCEL, osd::video_sdl3_accel)