mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 06:32:48 +00:00
2f36c94f3f
BFI support added for 180hz / 240hz / etc. Solves issue with image retention from voltage issues at 120hz BFI. Also disabled BFI while in menu, as if set to an incorrect value for the current refresh rate, could cause severe flickering and difficulty reverting to the correct value.
1868 lines
49 KiB
C
1868 lines
49 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
* Copyright (C) 2012-2014 - OV2
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define CINTERFACE
|
|
|
|
#ifdef _XBOX
|
|
#include <xtl.h>
|
|
#include <xgraphics.h>
|
|
#endif
|
|
|
|
#include <formats/image.h>
|
|
#include <compat/strl.h>
|
|
#include <compat/posix_string.h>
|
|
#include <file/file_path.h>
|
|
#include <string/stdstring.h>
|
|
#include <retro_math.h>
|
|
|
|
#include <d3d8.h>
|
|
|
|
#include "../../defines/d3d_defines.h"
|
|
#include "../common/d3d8_common.h"
|
|
#include "../common/d3d_common.h"
|
|
#include "../video_coord_array.h"
|
|
#include "../../configuration.h"
|
|
#include "../../retroarch.h"
|
|
#include "../../dynamic.h"
|
|
#include "../../frontend/frontend_driver.h"
|
|
|
|
#ifdef HAVE_THREADS
|
|
#include "../video_thread_wrapper.h"
|
|
#endif
|
|
|
|
#include "../common/win32_common.h"
|
|
|
|
#ifdef _XBOX
|
|
#define D3D8_PRESENTATIONINTERVAL D3DRS_PRESENTATIONINTERVAL
|
|
#endif
|
|
|
|
#ifdef HAVE_MENU
|
|
#include "../../menu/menu_driver.h"
|
|
#endif
|
|
|
|
#include "../font_driver.h"
|
|
|
|
#include "../../core.h"
|
|
#include "../../verbosity.h"
|
|
|
|
#ifdef __WINRT__
|
|
#error "UWP does not support D3D8"
|
|
#endif
|
|
|
|
static LPDIRECT3D8 g_pD3D8;
|
|
|
|
void *dinput;
|
|
|
|
typedef struct d3d8_renderchain
|
|
{
|
|
unsigned pixel_size;
|
|
LPDIRECT3DDEVICE8 dev;
|
|
const video_info_t *video_info;
|
|
LPDIRECT3DTEXTURE8 tex;
|
|
LPDIRECT3DVERTEXBUFFER8 vertex_buf;
|
|
unsigned last_width;
|
|
unsigned last_height;
|
|
void *vertex_decl;
|
|
unsigned tex_w;
|
|
unsigned tex_h;
|
|
uint64_t frame_count;
|
|
} d3d8_renderchain_t;
|
|
|
|
struct d3d8_texture_info
|
|
{
|
|
void *userdata;
|
|
void *data;
|
|
enum texture_filter_type type;
|
|
};
|
|
|
|
void d3d8_set_mvp(void *data, const void *mat_data)
|
|
{
|
|
struct d3d_matrix matrix;
|
|
LPDIRECT3DDEVICE8 d3dr = (LPDIRECT3DDEVICE8)data;
|
|
|
|
d3d_matrix_identity(&matrix);
|
|
|
|
d3d8_set_transform(d3dr, D3DTS_PROJECTION, (D3DMATRIX*)&matrix);
|
|
d3d8_set_transform(d3dr, D3DTS_VIEW, (D3DMATRIX*)&matrix);
|
|
|
|
if (mat_data)
|
|
d3d_matrix_transpose(&matrix, mat_data);
|
|
|
|
d3d8_set_transform(d3dr, D3DTS_WORLD, (D3DMATRIX*)&matrix);
|
|
}
|
|
|
|
static bool d3d8_renderchain_create_first_pass(
|
|
d3d8_video_t *d3d,
|
|
d3d8_renderchain_t *chain,
|
|
const video_info_t *info)
|
|
{
|
|
LPDIRECT3DDEVICE8 d3dr = (LPDIRECT3DDEVICE8)d3d->dev;
|
|
|
|
chain->vertex_buf = (LPDIRECT3DVERTEXBUFFER8)d3d8_vertex_buffer_new(d3dr, 4 * sizeof(Vertex),
|
|
D3DUSAGE_WRITEONLY,
|
|
D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE,
|
|
D3DPOOL_MANAGED,
|
|
NULL);
|
|
|
|
if (!chain->vertex_buf)
|
|
return false;
|
|
|
|
chain->tex = (LPDIRECT3DTEXTURE8)d3d8_texture_new(d3dr, NULL,
|
|
chain->tex_w, chain->tex_h, 1, 0,
|
|
info->rgb32
|
|
?
|
|
d3d8_get_xrgb8888_format() : d3d8_get_rgb565_format(),
|
|
D3DPOOL_MANAGED, 0, 0, 0, NULL, NULL,
|
|
false);
|
|
|
|
if (!chain->tex)
|
|
return false;
|
|
|
|
d3d8_set_sampler_address_u(d3dr, 0, D3DTADDRESS_BORDER);
|
|
d3d8_set_sampler_address_v(d3dr, 0, D3DTADDRESS_BORDER);
|
|
d3d8_set_render_state(d3dr, D3DRS_LIGHTING, 0);
|
|
d3d8_set_render_state(d3dr, D3DRS_CULLMODE, D3DCULL_NONE);
|
|
d3d8_set_render_state(d3dr, D3DRS_ZENABLE, FALSE);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void d3d8_renderchain_set_vertices(
|
|
d3d8_video_t *d3d,
|
|
d3d8_renderchain_t *chain,
|
|
unsigned pass,
|
|
unsigned vert_width, unsigned vert_height, uint64_t frame_count)
|
|
{
|
|
unsigned width, height;
|
|
|
|
video_driver_get_size(&width, &height);
|
|
|
|
if (chain->last_width != vert_width || chain->last_height != vert_height)
|
|
{
|
|
Vertex vert[4];
|
|
float tex_w, tex_h;
|
|
void *verts = NULL;
|
|
|
|
chain->last_width = vert_width;
|
|
chain->last_height = vert_height;
|
|
|
|
tex_w = vert_width;
|
|
tex_h = vert_height;
|
|
|
|
vert[0].x = 0.0f;
|
|
vert[0].y = 1.0f;
|
|
vert[0].z = 1.0f;
|
|
|
|
vert[1].x = 1.0f;
|
|
vert[1].y = 1.0f;
|
|
vert[1].z = 1.0f;
|
|
|
|
vert[2].x = 0.0f;
|
|
vert[2].y = 0.0f;
|
|
vert[2].z = 1.0f;
|
|
|
|
vert[3].x = 1.0f;
|
|
vert[3].y = 0.0f;
|
|
vert[3].z = 1.0f;
|
|
|
|
vert[0].u = 0.0f;
|
|
vert[0].v = 0.0f;
|
|
vert[1].v = 0.0f;
|
|
vert[2].u = 0.0f;
|
|
vert[1].u = tex_w;
|
|
vert[2].v = tex_h;
|
|
vert[3].u = tex_w;
|
|
vert[3].v = tex_h;
|
|
#ifndef _XBOX
|
|
vert[1].u /= chain->tex_w;
|
|
vert[2].v /= chain->tex_h;
|
|
vert[3].u /= chain->tex_w;
|
|
vert[3].v /= chain->tex_h;
|
|
#endif
|
|
|
|
vert[0].color = 0xFFFFFFFF;
|
|
vert[1].color = 0xFFFFFFFF;
|
|
vert[2].color = 0xFFFFFFFF;
|
|
vert[3].color = 0xFFFFFFFF;
|
|
|
|
verts = d3d8_vertex_buffer_lock(chain->vertex_buf);
|
|
memcpy(verts, vert, sizeof(vert));
|
|
d3d8_vertex_buffer_unlock(chain->vertex_buf);
|
|
}
|
|
}
|
|
|
|
static void d3d8_renderchain_blit_to_texture(
|
|
d3d8_renderchain_t *chain,
|
|
const void *frame,
|
|
unsigned width, unsigned height, unsigned pitch)
|
|
{
|
|
D3DLOCKED_RECT d3dlr;
|
|
LPDIRECT3DDEVICE8 d3dr = (LPDIRECT3DDEVICE8)chain->dev;
|
|
|
|
d3d8_frame_postprocess(chain);
|
|
|
|
if (chain->last_width != width || chain->last_height != height)
|
|
{
|
|
d3d8_lock_rectangle(chain->tex,
|
|
0, &d3dlr, NULL, chain->tex_h, D3DLOCK_NOSYSLOCK);
|
|
d3d8_lock_rectangle_clear(chain->tex,
|
|
0, &d3dlr, NULL, chain->tex_h, D3DLOCK_NOSYSLOCK);
|
|
}
|
|
|
|
/* Set the texture to NULL so D3D doesn't complain about it being in use... */
|
|
d3d8_set_texture(d3dr, 0, NULL);
|
|
|
|
if (d3d8_lock_rectangle(chain->tex, 0, &d3dlr, NULL, 0, 0))
|
|
{
|
|
d3d8_texture_blit(chain->pixel_size, chain->tex,
|
|
&d3dlr, frame, width, height, pitch);
|
|
d3d8_unlock_rectangle(chain->tex);
|
|
}
|
|
}
|
|
|
|
static void d3d8_renderchain_free(void *data)
|
|
{
|
|
d3d8_renderchain_t *chain = (d3d8_renderchain_t*)data;
|
|
|
|
if (!chain)
|
|
return;
|
|
|
|
if (chain->tex)
|
|
d3d8_texture_free(chain->tex);
|
|
d3d8_vertex_buffer_free(chain->vertex_buf, chain->vertex_decl);
|
|
|
|
free(chain);
|
|
}
|
|
|
|
static void d3d8_renderchain_viewport_info(
|
|
d3d8_video_t *d3d,
|
|
struct video_viewport *vp)
|
|
{
|
|
unsigned width, height;
|
|
|
|
if (!d3d || !vp)
|
|
return;
|
|
|
|
video_driver_get_size(&width, &height);
|
|
|
|
vp->x = d3d->final_viewport.X;
|
|
vp->y = d3d->final_viewport.Y;
|
|
vp->width = d3d->final_viewport.Width;
|
|
vp->height = d3d->final_viewport.Height;
|
|
|
|
vp->full_width = width;
|
|
vp->full_height = height;
|
|
}
|
|
|
|
static void d3d8_renderchain_render_pass(
|
|
d3d8_video_t *d3d, LPDIRECT3DDEVICE8 d3dr,
|
|
d3d8_renderchain_t *chain,
|
|
unsigned pass_index,
|
|
unsigned rotation)
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
bool video_smooth = settings->bools.video_smooth;
|
|
|
|
d3d8_set_texture(d3dr, 0, chain->tex);
|
|
d3d8_set_sampler_magfilter(d3dr, pass_index, video_smooth ?
|
|
D3DTEXF_LINEAR : D3DTEXF_POINT);
|
|
d3d8_set_sampler_minfilter(d3dr, pass_index, video_smooth ?
|
|
D3DTEXF_LINEAR : D3DTEXF_POINT);
|
|
|
|
d3d8_set_viewports(chain->dev, &d3d->final_viewport);
|
|
d3d8_set_vertex_shader(d3dr,
|
|
D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE,
|
|
NULL);
|
|
d3d8_set_stream_source(d3dr, 0, chain->vertex_buf, 0, sizeof(Vertex));
|
|
d3d8_set_mvp(d3d->dev, &d3d->mvp_rotate);
|
|
d3d8_draw_primitive(d3dr, D3DPT_TRIANGLESTRIP, 0, 2);
|
|
}
|
|
|
|
static bool d3d8_renderchain_render(
|
|
d3d8_video_t *d3d,
|
|
const void *frame,
|
|
unsigned frame_width, unsigned frame_height,
|
|
unsigned pitch, unsigned rotation)
|
|
{
|
|
LPDIRECT3DDEVICE8 d3dr = (LPDIRECT3DDEVICE8)d3d->dev;
|
|
d3d8_renderchain_t *chain = (d3d8_renderchain_t*)d3d->renderchain_data;
|
|
|
|
d3d8_renderchain_blit_to_texture(chain, frame, frame_width, frame_height, pitch);
|
|
d3d8_renderchain_set_vertices(d3d, chain, 1, frame_width, frame_height, chain->frame_count);
|
|
|
|
d3d8_renderchain_render_pass(d3d, d3dr, chain, 0, rotation);
|
|
|
|
chain->frame_count++;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool d3d8_renderchain_init(void *data,
|
|
const video_info_t *video_info,
|
|
void *dev_data,
|
|
const struct LinkInfo *link_info,
|
|
bool rgb32
|
|
)
|
|
{
|
|
unsigned width, height;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
LPDIRECT3DDEVICE8 d3dr = (LPDIRECT3DDEVICE8)d3d->dev;
|
|
d3d8_renderchain_t *chain = (d3d8_renderchain_t*)d3d->renderchain_data;
|
|
unsigned fmt = (rgb32) ? RETRO_PIXEL_FORMAT_XRGB8888 : RETRO_PIXEL_FORMAT_RGB565;
|
|
struct video_viewport *custom_vp = video_viewport_get_custom();
|
|
|
|
video_driver_get_size(&width, &height);
|
|
|
|
chain->dev = dev_data;
|
|
chain->pixel_size = (fmt == RETRO_PIXEL_FORMAT_RGB565)
|
|
? 2
|
|
: 4;
|
|
chain->tex_w = link_info->tex_w;
|
|
chain->tex_h = link_info->tex_h;
|
|
|
|
if (!d3d8_renderchain_create_first_pass(d3d, chain, video_info))
|
|
return false;
|
|
|
|
/* FIXME */
|
|
if (custom_vp->width == 0)
|
|
custom_vp->width = width;
|
|
|
|
if (custom_vp->height == 0)
|
|
custom_vp->height = height;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void *d3d8_renderchain_new(void)
|
|
{
|
|
d3d8_renderchain_t *renderchain = (d3d8_renderchain_t*)calloc(1, sizeof(*renderchain));
|
|
if (!renderchain)
|
|
return NULL;
|
|
|
|
return renderchain;
|
|
}
|
|
|
|
static bool d3d8_init_chain(d3d8_video_t *d3d, const video_info_t *video_info)
|
|
{
|
|
struct LinkInfo link_info;
|
|
unsigned current_width, current_height, out_width, out_height;
|
|
unsigned i = 0;
|
|
|
|
(void)i;
|
|
(void)current_width;
|
|
(void)current_height;
|
|
(void)out_width;
|
|
(void)out_height;
|
|
|
|
/* Setup information for first pass. */
|
|
link_info.pass = NULL;
|
|
link_info.tex_w = video_info->input_scale * RARCH_SCALE_BASE;
|
|
link_info.tex_h = video_info->input_scale * RARCH_SCALE_BASE;
|
|
link_info.pass = &d3d->shader.pass[0];
|
|
|
|
d3d->renderchain_data = d3d8_renderchain_new();
|
|
|
|
if (
|
|
!d3d8_renderchain_init(
|
|
d3d,
|
|
&d3d->video_info,
|
|
d3d->dev, &link_info,
|
|
d3d->video_info.rgb32)
|
|
)
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to init render chain.\n");
|
|
return false;
|
|
}
|
|
|
|
RARCH_LOG("[D3D8]: Renderchain driver: %s\n", "d3d8");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool d3d8_init_singlepass(d3d8_video_t *d3d)
|
|
{
|
|
struct video_shader_pass *pass = NULL;
|
|
|
|
if (!d3d)
|
|
return false;
|
|
|
|
memset(&d3d->shader, 0, sizeof(d3d->shader));
|
|
d3d->shader.passes = 1;
|
|
|
|
pass = (struct video_shader_pass*)
|
|
&d3d->shader.pass[0];
|
|
|
|
pass->fbo.valid = true;
|
|
pass->fbo.scale_y = 1.0;
|
|
pass->fbo.type_y = RARCH_SCALE_VIEWPORT;
|
|
pass->fbo.scale_x = pass->fbo.scale_y;
|
|
pass->fbo.type_x = pass->fbo.type_y;
|
|
|
|
if (!string_is_empty(d3d->shader_path))
|
|
strlcpy(pass->source.path, d3d->shader_path,
|
|
sizeof(pass->source.path));
|
|
|
|
return true;
|
|
}
|
|
|
|
static void d3d8_viewport_info(void *data, struct video_viewport *vp)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (d3d)
|
|
d3d8_renderchain_viewport_info(d3d, vp);
|
|
}
|
|
|
|
static void d3d8_overlay_render(d3d8_video_t *d3d,
|
|
unsigned width, unsigned height,
|
|
overlay_t *overlay, bool force_linear)
|
|
{
|
|
D3DVIEWPORT8 vp_full;
|
|
struct video_viewport vp;
|
|
void *verts;
|
|
unsigned i;
|
|
Vertex vert[4];
|
|
enum D3DTEXTUREFILTERTYPE filter_type = D3DTEXF_LINEAR;
|
|
|
|
if (!d3d || !overlay || !overlay->tex)
|
|
return;
|
|
|
|
if (!overlay->vert_buf)
|
|
{
|
|
overlay->vert_buf = d3d8_vertex_buffer_new(
|
|
d3d->dev, sizeof(vert), D3DUSAGE_WRITEONLY,
|
|
D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE,
|
|
D3DPOOL_MANAGED, NULL);
|
|
|
|
if (!overlay->vert_buf)
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
vert[i].z = 0.5f;
|
|
vert[i].color = (((uint32_t)(overlay->alpha_mod * 0xFF)) << 24) | 0xFFFFFF;
|
|
}
|
|
|
|
d3d8_viewport_info(d3d, &vp);
|
|
|
|
vert[0].x = overlay->vert_coords[0];
|
|
vert[1].x = overlay->vert_coords[0] + overlay->vert_coords[2];
|
|
vert[2].x = overlay->vert_coords[0];
|
|
vert[3].x = overlay->vert_coords[0] + overlay->vert_coords[2];
|
|
vert[0].y = overlay->vert_coords[1];
|
|
vert[1].y = overlay->vert_coords[1];
|
|
vert[2].y = overlay->vert_coords[1] + overlay->vert_coords[3];
|
|
vert[3].y = overlay->vert_coords[1] + overlay->vert_coords[3];
|
|
|
|
vert[0].u = overlay->tex_coords[0];
|
|
vert[1].u = overlay->tex_coords[0] + overlay->tex_coords[2];
|
|
vert[2].u = overlay->tex_coords[0];
|
|
vert[3].u = overlay->tex_coords[0] + overlay->tex_coords[2];
|
|
vert[0].v = overlay->tex_coords[1];
|
|
vert[1].v = overlay->tex_coords[1];
|
|
vert[2].v = overlay->tex_coords[1] + overlay->tex_coords[3];
|
|
vert[3].v = overlay->tex_coords[1] + overlay->tex_coords[3];
|
|
|
|
verts = d3d8_vertex_buffer_lock(overlay->vert_buf);
|
|
memcpy(verts, vert, sizeof(vert));
|
|
d3d8_vertex_buffer_unlock(overlay->vert_buf);
|
|
|
|
d3d8_enable_blend_func(d3d->dev);
|
|
d3d8_set_vertex_shader(d3d->dev,
|
|
D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE,
|
|
NULL);
|
|
|
|
d3d8_set_stream_source(d3d->dev, 0, overlay->vert_buf,
|
|
0, sizeof(*vert));
|
|
|
|
if (overlay->fullscreen)
|
|
{
|
|
|
|
vp_full.X = 0;
|
|
vp_full.Y = 0;
|
|
vp_full.Width = width;
|
|
vp_full.Height = height;
|
|
vp_full.MinZ = 0.0f;
|
|
vp_full.MaxZ = 1.0f;
|
|
d3d8_set_viewports(d3d->dev, &vp_full);
|
|
}
|
|
|
|
if (!force_linear)
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
bool menu_linear_filter = settings->bools.menu_linear_filter;
|
|
|
|
if (!menu_linear_filter)
|
|
filter_type = D3DTEXF_POINT;
|
|
}
|
|
|
|
/* Render overlay. */
|
|
d3d8_set_texture(d3d->dev, 0, overlay->tex);
|
|
d3d8_set_sampler_address_u(d3d->dev, 0, D3DTADDRESS_BORDER);
|
|
d3d8_set_sampler_address_v(d3d->dev, 0, D3DTADDRESS_BORDER);
|
|
d3d8_set_sampler_minfilter(d3d->dev, 0, filter_type);
|
|
d3d8_set_sampler_magfilter(d3d->dev, 0, filter_type);
|
|
d3d8_draw_primitive(d3d->dev, D3DPT_TRIANGLESTRIP, 0, 2);
|
|
|
|
/* Restore previous state. */
|
|
d3d8_disable_blend_func(d3d->dev);
|
|
|
|
d3d8_set_viewports(d3d->dev, &d3d->final_viewport);
|
|
}
|
|
|
|
static void d3d8_free_overlay(d3d8_video_t *d3d, overlay_t *overlay)
|
|
{
|
|
if (!d3d)
|
|
return;
|
|
|
|
d3d8_texture_free(overlay->tex);
|
|
d3d8_vertex_buffer_free(overlay->vert_buf, NULL);
|
|
}
|
|
|
|
static void d3d8_deinit_chain(d3d8_video_t *d3d)
|
|
{
|
|
d3d8_renderchain_free(d3d->renderchain_data);
|
|
|
|
d3d->renderchain_data = NULL;
|
|
}
|
|
|
|
static void d3d8_deinitialize(d3d8_video_t *d3d)
|
|
{
|
|
if (!d3d)
|
|
return;
|
|
|
|
font_driver_free_osd();
|
|
|
|
d3d8_deinit_chain(d3d);
|
|
d3d8_vertex_buffer_free(d3d->menu_display.buffer, d3d->menu_display.decl);
|
|
d3d->menu_display.buffer = NULL;
|
|
d3d->menu_display.decl = NULL;
|
|
}
|
|
|
|
#define FS_PRESENTINTERVAL(pp) ((pp)->FullScreen_PresentationInterval)
|
|
|
|
static D3DFORMAT d3d8_get_color_format_backbuffer(bool rgb32, bool windowed)
|
|
{
|
|
D3DFORMAT fmt = D3DFMT_X8R8G8B8;
|
|
#ifdef _XBOX
|
|
if (!rgb32)
|
|
fmt = d3d8_get_rgb565_format();
|
|
#else
|
|
if (windowed)
|
|
{
|
|
D3DDISPLAYMODE display_mode;
|
|
if (d3d8_get_adapter_display_mode(g_pD3D8, 0, &display_mode))
|
|
fmt = display_mode.Format;
|
|
}
|
|
#endif
|
|
return fmt;
|
|
}
|
|
|
|
static bool d3d8_is_windowed_enable(bool info_fullscreen)
|
|
{
|
|
#ifndef _XBOX
|
|
settings_t *settings = config_get_ptr();
|
|
if (!info_fullscreen)
|
|
return true;
|
|
if (settings)
|
|
return settings->bools.video_windowed_fullscreen;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
#ifdef _XBOX
|
|
static void d3d8_get_video_size(d3d8_video_t *d3d,
|
|
unsigned *width, unsigned *height)
|
|
{
|
|
DWORD video_mode = XGetVideoFlags();
|
|
|
|
*width = 640;
|
|
*height = 480;
|
|
|
|
d3d->widescreen_mode = false;
|
|
|
|
/* Only valid in PAL mode, not valid for HDTV modes! */
|
|
|
|
if(XGetVideoStandard() == XC_VIDEO_STANDARD_PAL_I)
|
|
{
|
|
/* Check for 16:9 mode (PAL REGION) */
|
|
if(video_mode & XC_VIDEO_FLAGS_WIDESCREEN)
|
|
{
|
|
*width = 720;
|
|
/* 60 Hz, 720x480i */
|
|
if(video_mode & XC_VIDEO_FLAGS_PAL_60Hz)
|
|
*height = 480;
|
|
else /* 50 Hz, 720x576i */
|
|
*height = 576;
|
|
d3d->widescreen_mode = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check for 16:9 mode (NTSC REGIONS) */
|
|
if(video_mode & XC_VIDEO_FLAGS_WIDESCREEN)
|
|
{
|
|
*width = 720;
|
|
*height = 480;
|
|
d3d->widescreen_mode = true;
|
|
}
|
|
}
|
|
|
|
if(XGetAVPack() == XC_AV_PACK_HDTV)
|
|
{
|
|
if(video_mode & XC_VIDEO_FLAGS_HDTV_480p)
|
|
{
|
|
*width = 640;
|
|
*height = 480;
|
|
d3d->widescreen_mode = false;
|
|
d3d->resolution_hd_enable = true;
|
|
}
|
|
else if(video_mode & XC_VIDEO_FLAGS_HDTV_720p)
|
|
{
|
|
*width = 1280;
|
|
*height = 720;
|
|
d3d->widescreen_mode = true;
|
|
d3d->resolution_hd_enable = true;
|
|
}
|
|
else if(video_mode & XC_VIDEO_FLAGS_HDTV_1080i)
|
|
{
|
|
*width = 1920;
|
|
*height = 1080;
|
|
d3d->widescreen_mode = true;
|
|
d3d->resolution_hd_enable = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void d3d8_make_d3dpp(void *data,
|
|
const video_info_t *info, void *_d3dpp)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
D3DPRESENT_PARAMETERS *d3dpp = (D3DPRESENT_PARAMETERS*)_d3dpp;
|
|
bool windowed_enable = d3d8_is_windowed_enable(info->fullscreen);
|
|
|
|
memset(d3dpp, 0, sizeof(*d3dpp));
|
|
|
|
d3dpp->Windowed = windowed_enable;
|
|
FS_PRESENTINTERVAL(d3dpp) = D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
|
|
if (info->vsync)
|
|
{
|
|
settings_t *settings = config_get_ptr();
|
|
unsigned video_swap_interval = settings->uints.video_swap_interval;
|
|
|
|
switch (video_swap_interval)
|
|
{
|
|
default:
|
|
case 1:
|
|
FS_PRESENTINTERVAL(d3dpp) = D3DPRESENT_INTERVAL_ONE;
|
|
break;
|
|
case 2:
|
|
FS_PRESENTINTERVAL(d3dpp) = D3DPRESENT_INTERVAL_TWO;
|
|
break;
|
|
case 3:
|
|
FS_PRESENTINTERVAL(d3dpp) = D3DPRESENT_INTERVAL_THREE;
|
|
break;
|
|
case 4:
|
|
FS_PRESENTINTERVAL(d3dpp) = D3DPRESENT_INTERVAL_FOUR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* PresentationInterval must be zero for windowed mode on DX8. */
|
|
if (d3dpp->Windowed)
|
|
FS_PRESENTINTERVAL(d3dpp) = D3DPRESENT_INTERVAL_DEFAULT;
|
|
|
|
d3dpp->SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
d3dpp->BackBufferCount = 2;
|
|
d3dpp->BackBufferFormat = d3d8_get_color_format_backbuffer(
|
|
info->rgb32, windowed_enable);
|
|
#ifndef _XBOX
|
|
d3dpp->hDeviceWindow = win32_get_window();
|
|
#endif
|
|
|
|
if (!windowed_enable)
|
|
{
|
|
#ifdef _XBOX
|
|
unsigned width = 0;
|
|
unsigned height = 0;
|
|
|
|
d3d8_get_video_size(d3d, &width, &height);
|
|
video_driver_set_size(width, height);
|
|
#endif
|
|
video_driver_get_size(&d3dpp->BackBufferWidth,
|
|
&d3dpp->BackBufferHeight);
|
|
}
|
|
|
|
#ifdef _XBOX
|
|
d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE;
|
|
d3dpp->EnableAutoDepthStencil = FALSE;
|
|
{
|
|
/* Get the "video mode" */
|
|
DWORD video_mode = XGetVideoFlags();
|
|
|
|
/* Check if we are able to use progressive mode. */
|
|
if (video_mode & XC_VIDEO_FLAGS_HDTV_480p)
|
|
d3dpp->Flags = D3DPRESENTFLAG_PROGRESSIVE;
|
|
else
|
|
d3dpp->Flags = D3DPRESENTFLAG_INTERLACED;
|
|
|
|
/* Only valid in PAL mode, not valid for HDTV modes. */
|
|
if (XGetVideoStandard() == XC_VIDEO_STANDARD_PAL_I)
|
|
{
|
|
if (video_mode & XC_VIDEO_FLAGS_PAL_60Hz)
|
|
d3dpp->FullScreen_RefreshRateInHz = 60;
|
|
else
|
|
d3dpp->FullScreen_RefreshRateInHz = 50;
|
|
}
|
|
|
|
if (XGetAVPack() == XC_AV_PACK_HDTV)
|
|
{
|
|
if (video_mode & XC_VIDEO_FLAGS_HDTV_480p)
|
|
d3dpp->Flags = D3DPRESENTFLAG_PROGRESSIVE;
|
|
else if (video_mode & XC_VIDEO_FLAGS_HDTV_720p)
|
|
d3dpp->Flags = D3DPRESENTFLAG_PROGRESSIVE;
|
|
else if (video_mode & XC_VIDEO_FLAGS_HDTV_1080i)
|
|
d3dpp->Flags = D3DPRESENTFLAG_INTERLACED;
|
|
}
|
|
|
|
#if 0
|
|
if (d3d->widescreen_mode)
|
|
d3dpp->Flags |= D3DPRESENTFLAG_WIDESCREEN;
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static bool d3d8_init_base(void *data, const video_info_t *info)
|
|
{
|
|
D3DPRESENT_PARAMETERS d3dpp;
|
|
HWND focus_window = NULL;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
#ifndef _XBOX
|
|
focus_window = win32_get_window();
|
|
#endif
|
|
|
|
memset(&d3dpp, 0, sizeof(d3dpp));
|
|
|
|
g_pD3D8 = (LPDIRECT3D8)d3d8_create();
|
|
|
|
/* this needs g_pD3D created first */
|
|
d3d8_make_d3dpp(d3d, info, &d3dpp);
|
|
|
|
if (!g_pD3D8)
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to create D3D interface.\n");
|
|
return false;
|
|
}
|
|
|
|
if (!d3d8_create_device(&d3d->dev, &d3dpp,
|
|
g_pD3D8,
|
|
focus_window,
|
|
d3d->cur_mon_id)
|
|
)
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to initialize device.\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void d3d8_calculate_rect(void *data,
|
|
unsigned *width, unsigned *height,
|
|
int *x, int *y,
|
|
bool force_full,
|
|
bool allow_rotate)
|
|
{
|
|
float device_aspect = (float)*width / *height;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
settings_t *settings = config_get_ptr();
|
|
bool video_scale_integer = settings->bools.video_scale_integer;
|
|
unsigned aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
|
|
|
|
video_driver_get_size(width, height);
|
|
|
|
*x = 0;
|
|
*y = 0;
|
|
|
|
if (video_scale_integer && !force_full)
|
|
{
|
|
struct video_viewport vp;
|
|
|
|
vp.x = 0;
|
|
vp.y = 0;
|
|
vp.width = 0;
|
|
vp.height = 0;
|
|
vp.full_width = 0;
|
|
vp.full_height = 0;
|
|
|
|
video_viewport_get_scaled_integer(&vp,
|
|
*width,
|
|
*height,
|
|
video_driver_get_aspect_ratio(),
|
|
d3d->keep_aspect);
|
|
|
|
*x = vp.x;
|
|
*y = vp.y;
|
|
*width = vp.width;
|
|
*height = vp.height;
|
|
}
|
|
else if (d3d->keep_aspect && !force_full)
|
|
{
|
|
float desired_aspect = video_driver_get_aspect_ratio();
|
|
|
|
#if defined(HAVE_MENU)
|
|
if (aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
|
|
{
|
|
video_viewport_t *custom = video_viewport_get_custom();
|
|
|
|
*x = custom->x;
|
|
*y = custom->y;
|
|
*width = custom->width;
|
|
*height = custom->height;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
float delta;
|
|
|
|
if (fabsf(device_aspect - desired_aspect) < 0.0001f)
|
|
{
|
|
/* If the aspect ratios of screen and desired aspect
|
|
* ratio are sufficiently equal (floating point stuff),
|
|
* assume they are actually equal.
|
|
*/
|
|
}
|
|
else if (device_aspect > desired_aspect)
|
|
{
|
|
delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f;
|
|
*x = (int)(roundf(*width * (0.5f - delta)));
|
|
*width = (unsigned)(roundf(2.0f * (*width) * delta));
|
|
}
|
|
else
|
|
{
|
|
delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f;
|
|
*y = (int)(roundf(*height * (0.5f - delta)));
|
|
*height = (unsigned)(roundf(2.0f * (*height) * delta));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void d3d8_set_viewport(void *data,
|
|
unsigned width, unsigned height,
|
|
bool force_full,
|
|
bool allow_rotate)
|
|
{
|
|
struct d3d_matrix proj, ortho, rot, matrix;
|
|
int x = 0;
|
|
int y = 0;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
d3d8_calculate_rect(data, &width, &height, &x, &y,
|
|
force_full, allow_rotate);
|
|
|
|
/* D3D doesn't support negative X/Y viewports ... */
|
|
if (x < 0)
|
|
x = 0;
|
|
if (y < 0)
|
|
y = 0;
|
|
|
|
d3d->final_viewport.X = x;
|
|
d3d->final_viewport.Y = y;
|
|
d3d->final_viewport.Width = width;
|
|
d3d->final_viewport.Height = height;
|
|
d3d->final_viewport.MinZ = 0.0f;
|
|
d3d->final_viewport.MaxZ = 0.0f;
|
|
|
|
d3d_matrix_ortho_off_center_lh(&ortho, 0, 1, 0, 1, 0.0f, 1.0f);
|
|
d3d_matrix_identity(&rot);
|
|
d3d_matrix_rotation_z(&rot, d3d->dev_rotation * (M_PI / 2.0));
|
|
d3d_matrix_multiply(&proj, &ortho, &rot);
|
|
d3d_matrix_transpose(&d3d->mvp, &ortho);
|
|
d3d_matrix_transpose(&d3d->mvp_rotate, &matrix);
|
|
}
|
|
|
|
static bool d3d8_initialize(d3d8_video_t *d3d, const video_info_t *info)
|
|
{
|
|
unsigned width, height;
|
|
bool ret = true;
|
|
settings_t *settings = config_get_ptr();
|
|
|
|
if (!d3d)
|
|
return false;
|
|
|
|
if (!g_pD3D8)
|
|
ret = d3d8_init_base(d3d, info);
|
|
else if (d3d->needs_restore)
|
|
{
|
|
D3DPRESENT_PARAMETERS d3dpp;
|
|
|
|
d3d8_make_d3dpp(d3d, info, &d3dpp);
|
|
|
|
/* the D3DX font driver uses POOL_DEFAULT resources
|
|
* and will prevent a clean reset here
|
|
* another approach would be to keep track of all created D3D
|
|
* font objects and free/realloc them around the d3d_reset call */
|
|
#ifdef HAVE_MENU
|
|
menu_driver_ctl(RARCH_MENU_CTL_DEINIT, NULL);
|
|
#endif
|
|
|
|
if (!d3d8_reset(d3d->dev, &d3dpp))
|
|
{
|
|
d3d8_deinitialize(d3d);
|
|
d3d8_device_free(NULL, g_pD3D8);
|
|
g_pD3D8 = NULL;
|
|
|
|
ret = d3d8_init_base(d3d, info);
|
|
if (ret)
|
|
RARCH_LOG("[D3D8]: Recovered from dead state.\n");
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
menu_driver_init(info->is_threaded);
|
|
#endif
|
|
}
|
|
|
|
if (!ret)
|
|
return ret;
|
|
|
|
if (!d3d8_init_chain(d3d, info))
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to initialize render chain.\n");
|
|
return false;
|
|
}
|
|
|
|
video_driver_get_size(&width, &height);
|
|
d3d8_set_viewport(d3d,
|
|
width, height, false, true);
|
|
|
|
font_driver_init_osd(d3d, info,
|
|
false,
|
|
info->is_threaded,
|
|
FONT_DRIVER_RENDER_D3D8_API);
|
|
|
|
d3d->menu_display.offset = 0;
|
|
d3d->menu_display.size = 1024;
|
|
d3d->menu_display.buffer = d3d8_vertex_buffer_new(
|
|
d3d->dev, d3d->menu_display.size * sizeof(Vertex),
|
|
D3DUSAGE_WRITEONLY,
|
|
D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE,
|
|
D3DPOOL_DEFAULT,
|
|
NULL);
|
|
|
|
if (!d3d->menu_display.buffer)
|
|
return false;
|
|
|
|
d3d_matrix_ortho_off_center_lh(&d3d->mvp_transposed, 0, 1, 0, 1, 0, 1);
|
|
d3d_matrix_transpose(&d3d->mvp, &d3d->mvp_transposed);
|
|
|
|
d3d8_set_render_state(d3d->dev, D3DRS_CULLMODE, D3DCULL_NONE);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool d3d8_restore(void *data)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (!d3d)
|
|
return false;
|
|
|
|
d3d8_deinitialize(d3d);
|
|
|
|
if (!d3d8_initialize(d3d, &d3d->video_info))
|
|
{
|
|
RARCH_ERR("[D3D8]: Restore error.\n");
|
|
return false;
|
|
}
|
|
|
|
d3d->needs_restore = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void d3d8_set_nonblock_state(void *data, bool state,
|
|
bool adaptive_vsync_enabled,
|
|
unsigned swap_interval)
|
|
{
|
|
int interval = 0;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (!d3d)
|
|
return;
|
|
|
|
if (!state)
|
|
interval = 1;
|
|
d3d->video_info.vsync = !state;
|
|
|
|
#ifdef _XBOX
|
|
d3d8_set_render_state(d3d->dev, D3D8_PRESENTATIONINTERVAL,
|
|
interval ?
|
|
D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE
|
|
);
|
|
#else
|
|
d3d->needs_restore = true;
|
|
d3d8_restore(d3d);
|
|
#endif
|
|
}
|
|
|
|
static bool d3d8_set_resize(d3d8_video_t *d3d,
|
|
unsigned new_width, unsigned new_height)
|
|
{
|
|
/* No changes? */
|
|
if ( (new_width == d3d->video_info.width)
|
|
&& (new_height == d3d->video_info.height))
|
|
return false;
|
|
|
|
RARCH_LOG("[D3D8]: Resize %ux%u.\n", new_width, new_height);
|
|
d3d->video_info.width = new_width;
|
|
d3d->video_info.height = new_height;
|
|
video_driver_set_size(new_width, new_height);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool d3d8_alive(void *data)
|
|
{
|
|
unsigned temp_width = 0;
|
|
unsigned temp_height = 0;
|
|
bool ret = false;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
bool quit = false;
|
|
bool resize = false;
|
|
|
|
/* Needed because some context drivers don't track their sizes */
|
|
video_driver_get_size(&temp_width, &temp_height);
|
|
|
|
win32_check_window(NULL, &quit, &resize, &temp_width, &temp_height);
|
|
|
|
if (quit)
|
|
d3d->quitting = quit;
|
|
|
|
if (resize)
|
|
{
|
|
d3d->should_resize = true;
|
|
d3d8_set_resize(d3d, temp_width, temp_height);
|
|
d3d8_restore(d3d);
|
|
}
|
|
|
|
ret = !quit;
|
|
|
|
if (temp_width != 0 && temp_height != 0)
|
|
video_driver_set_size(temp_width, temp_height);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool d3d8_suppress_screensaver(void *data, bool enable)
|
|
{
|
|
#ifdef _XBOX
|
|
return true;
|
|
#else
|
|
return win32_suppress_screensaver(data, enable);
|
|
#endif
|
|
}
|
|
|
|
static void d3d8_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (!d3d)
|
|
return;
|
|
|
|
d3d->keep_aspect = true;
|
|
d3d->should_resize = true;
|
|
}
|
|
|
|
static void d3d8_apply_state_changes(void *data)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
if (d3d)
|
|
d3d->should_resize = true;
|
|
}
|
|
|
|
static void d3d8_set_osd_msg(void *data,
|
|
const char *msg,
|
|
const void *params, void *font)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
d3d8_begin_scene(d3d->dev);
|
|
font_driver_render_msg(d3d, msg, params, font);
|
|
d3d8_end_scene(d3d->dev);
|
|
}
|
|
|
|
static bool d3d8_init_internal(d3d8_video_t *d3d,
|
|
const video_info_t *info, input_driver_t **input,
|
|
void **input_data)
|
|
{
|
|
#ifdef HAVE_MONITOR
|
|
bool windowed_full;
|
|
RECT mon_rect;
|
|
MONITORINFOEX current_mon;
|
|
HMONITOR hm_to_use;
|
|
#endif
|
|
#ifdef HAVE_WINDOW
|
|
DWORD style;
|
|
unsigned win_width = 0;
|
|
unsigned win_height = 0;
|
|
RECT rect = {0};
|
|
#endif
|
|
unsigned full_x = 0;
|
|
unsigned full_y = 0;
|
|
settings_t *settings = config_get_ptr();
|
|
overlay_t *menu = (overlay_t*)calloc(1, sizeof(*menu));
|
|
|
|
if (!menu)
|
|
return false;
|
|
|
|
d3d->menu = menu;
|
|
d3d->cur_mon_id = 0;
|
|
d3d->menu->tex_coords[0] = 0;
|
|
d3d->menu->tex_coords[1] = 0;
|
|
d3d->menu->tex_coords[2] = 1;
|
|
d3d->menu->tex_coords[3] = 1;
|
|
d3d->menu->vert_coords[0] = 0;
|
|
d3d->menu->vert_coords[1] = 1;
|
|
d3d->menu->vert_coords[2] = 1;
|
|
d3d->menu->vert_coords[3] = -1;
|
|
|
|
#ifdef HAVE_WINDOW
|
|
memset(&d3d->windowClass, 0, sizeof(d3d->windowClass));
|
|
d3d->windowClass.lpfnWndProc = wnd_proc_d3d_common;
|
|
#ifdef HAVE_DINPUT
|
|
if (string_is_equal(settings->arrays.input_driver, "dinput"))
|
|
d3d->windowClass.lpfnWndProc = wnd_proc_d3d_dinput;
|
|
#endif
|
|
win32_window_init(&d3d->windowClass, true, NULL);
|
|
#endif
|
|
|
|
#ifdef HAVE_MONITOR
|
|
win32_monitor_info(¤t_mon, &hm_to_use, &d3d->cur_mon_id);
|
|
|
|
mon_rect = current_mon.rcMonitor;
|
|
g_win32_resize_width = info->width;
|
|
g_win32_resize_height = info->height;
|
|
|
|
windowed_full = settings->bools.video_windowed_fullscreen;
|
|
|
|
full_x = (windowed_full || info->width == 0) ?
|
|
(mon_rect.right - mon_rect.left) : info->width;
|
|
full_y = (windowed_full || info->height == 0) ?
|
|
(mon_rect.bottom - mon_rect.top) : info->height;
|
|
|
|
RARCH_LOG("[D3D8]: Monitor size: %dx%d.\n",
|
|
(int)(mon_rect.right - mon_rect.left),
|
|
(int)(mon_rect.bottom - mon_rect.top));
|
|
#else
|
|
d3d8_get_video_size(d3d, &full_x, &full_y);
|
|
#endif
|
|
{
|
|
unsigned new_width = info->fullscreen ? full_x : info->width;
|
|
unsigned new_height = info->fullscreen ? full_y : info->height;
|
|
video_driver_set_size(new_width, new_height);
|
|
}
|
|
|
|
#ifdef HAVE_WINDOW
|
|
video_driver_get_size(&win_width, &win_height);
|
|
|
|
win32_set_style(¤t_mon, &hm_to_use, &win_width, &win_height,
|
|
info->fullscreen, windowed_full, &rect, &mon_rect, &style);
|
|
|
|
win32_window_create(d3d, style, &mon_rect, win_width,
|
|
win_height, info->fullscreen);
|
|
|
|
win32_set_window(&win_width, &win_height, info->fullscreen,
|
|
windowed_full, &rect);
|
|
#endif
|
|
|
|
if (!d3d8_init_singlepass(d3d))
|
|
return false;
|
|
|
|
d3d->video_info = *info;
|
|
if (!d3d8_initialize(d3d, &d3d->video_info))
|
|
return false;
|
|
|
|
d3d_input_driver(settings->arrays.input_driver, settings->arrays.input_joypad_driver, input, input_data);
|
|
|
|
RARCH_LOG("[D3D8]: Init complete.\n");
|
|
return true;
|
|
}
|
|
|
|
static void d3d8_set_rotation(void *data, unsigned rot)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (!d3d)
|
|
return;
|
|
|
|
d3d->dev_rotation = rot;
|
|
d3d->should_resize = true;
|
|
}
|
|
|
|
static void *d3d8_init(const video_info_t *info,
|
|
input_driver_t **input, void **input_data)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)calloc(1, sizeof(*d3d));
|
|
|
|
if (!d3d)
|
|
return NULL;
|
|
|
|
if (!d3d8_initialize_symbols(GFX_CTX_DIRECT3D8_API))
|
|
{
|
|
free(d3d);
|
|
return NULL;
|
|
}
|
|
|
|
#ifndef _XBOX
|
|
win32_window_reset();
|
|
win32_monitor_init();
|
|
#endif
|
|
|
|
/* Default values */
|
|
d3d->dev = NULL;
|
|
d3d->dev_rotation = 0;
|
|
d3d->needs_restore = false;
|
|
#ifdef HAVE_OVERLAY
|
|
d3d->overlays_enabled = false;
|
|
#endif
|
|
d3d->should_resize = false;
|
|
d3d->menu = NULL;
|
|
|
|
if (!d3d8_init_internal(d3d, info, input, input_data))
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to init D3D.\n");
|
|
free(d3d);
|
|
return NULL;
|
|
}
|
|
|
|
d3d->keep_aspect = info->force_aspect;
|
|
|
|
return d3d;
|
|
}
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
static void d3d8_free_overlays(d3d8_video_t *d3d)
|
|
{
|
|
unsigned i;
|
|
|
|
if (!d3d)
|
|
return;
|
|
|
|
for (i = 0; i < d3d->overlays_size; i++)
|
|
d3d8_free_overlay(d3d, &d3d->overlays[i]);
|
|
free(d3d->overlays);
|
|
d3d->overlays = NULL;
|
|
d3d->overlays_size = 0;
|
|
}
|
|
#endif
|
|
|
|
static void d3d8_free(void *data)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (!d3d)
|
|
return;
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
d3d8_free_overlays(d3d);
|
|
if (d3d->overlays)
|
|
free(d3d->overlays);
|
|
d3d->overlays = NULL;
|
|
d3d->overlays_size = 0;
|
|
#endif
|
|
|
|
d3d8_free_overlay(d3d, d3d->menu);
|
|
if (d3d->menu)
|
|
free(d3d->menu);
|
|
d3d->menu = NULL;
|
|
|
|
d3d8_deinitialize(d3d);
|
|
|
|
if (!string_is_empty(d3d->shader_path))
|
|
free(d3d->shader_path);
|
|
|
|
d3d->shader_path = NULL;
|
|
d3d8_device_free(d3d->dev, g_pD3D8);
|
|
d3d->dev = NULL;
|
|
g_pD3D8 = NULL;
|
|
|
|
d3d8_deinitialize_symbols();
|
|
|
|
#ifndef _XBOX
|
|
win32_monitor_from_window();
|
|
win32_destroy_window();
|
|
#endif
|
|
free(d3d);
|
|
}
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
static void d3d8_overlay_tex_geom(
|
|
void *data,
|
|
unsigned index,
|
|
float x, float y,
|
|
float w, float h)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
if (!d3d)
|
|
return;
|
|
|
|
d3d->overlays[index].tex_coords[0] = x;
|
|
d3d->overlays[index].tex_coords[1] = y;
|
|
d3d->overlays[index].tex_coords[2] = w;
|
|
d3d->overlays[index].tex_coords[3] = h;
|
|
#ifdef _XBOX
|
|
d3d->overlays[index].tex_coords[0] *= d3d->overlays[index].tex_w;
|
|
d3d->overlays[index].tex_coords[1] *= d3d->overlays[index].tex_h;
|
|
d3d->overlays[index].tex_coords[2] *= d3d->overlays[index].tex_w;
|
|
d3d->overlays[index].tex_coords[3] *= d3d->overlays[index].tex_h;
|
|
#endif
|
|
}
|
|
|
|
static void d3d8_overlay_vertex_geom(
|
|
void *data,
|
|
unsigned index,
|
|
float x, float y,
|
|
float w, float h)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
if (!d3d)
|
|
return;
|
|
|
|
y = 1.0f - y;
|
|
h = -h;
|
|
d3d->overlays[index].vert_coords[0] = x;
|
|
d3d->overlays[index].vert_coords[1] = y;
|
|
d3d->overlays[index].vert_coords[2] = w;
|
|
d3d->overlays[index].vert_coords[3] = h;
|
|
}
|
|
|
|
static bool d3d8_overlay_load(void *data,
|
|
const void *image_data, unsigned num_images)
|
|
{
|
|
unsigned i, y;
|
|
overlay_t *new_overlays = NULL;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
const struct texture_image *images = (const struct texture_image*)
|
|
image_data;
|
|
|
|
if (!d3d)
|
|
return false;
|
|
|
|
d3d8_free_overlays(d3d);
|
|
d3d->overlays = (overlay_t*)calloc(num_images, sizeof(*d3d->overlays));
|
|
d3d->overlays_size = num_images;
|
|
|
|
for (i = 0; i < num_images; i++)
|
|
{
|
|
D3DLOCKED_RECT d3dlr;
|
|
unsigned width = images[i].width;
|
|
unsigned height = images[i].height;
|
|
overlay_t *overlay = (overlay_t*)&d3d->overlays[i];
|
|
|
|
overlay->tex = d3d8_texture_new(d3d->dev, NULL,
|
|
width, height, 1,
|
|
0,
|
|
d3d8_get_argb8888_format(),
|
|
D3DPOOL_MANAGED, 0, 0, 0,
|
|
NULL, NULL, false);
|
|
|
|
if (!overlay->tex)
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to create overlay texture\n");
|
|
return false;
|
|
}
|
|
|
|
if (d3d8_lock_rectangle(overlay->tex, 0, &d3dlr,
|
|
NULL, 0, D3DLOCK_NOSYSLOCK))
|
|
{
|
|
uint32_t *dst = (uint32_t*)(d3dlr.pBits);
|
|
const uint32_t *src = images[i].pixels;
|
|
unsigned pitch = d3dlr.Pitch >> 2;
|
|
|
|
for (y = 0; y < height; y++, dst += pitch, src += width)
|
|
memcpy(dst, src, width << 2);
|
|
d3d8_unlock_rectangle(overlay->tex);
|
|
}
|
|
|
|
overlay->tex_w = width;
|
|
overlay->tex_h = height;
|
|
|
|
/* Default. Stretch to whole screen. */
|
|
d3d8_overlay_tex_geom(d3d, i, 0, 0, 1, 1);
|
|
d3d8_overlay_vertex_geom(d3d, i, 0, 0, 1, 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void d3d8_overlay_enable(void *data, bool state)
|
|
{
|
|
unsigned i;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (!d3d)
|
|
return;
|
|
|
|
for (i = 0; i < d3d->overlays_size; i++)
|
|
d3d->overlays_enabled = state;
|
|
|
|
#ifndef _XBOX
|
|
win32_show_cursor(d3d, state);
|
|
#endif
|
|
}
|
|
|
|
static void d3d8_overlay_full_screen(void *data, bool enable)
|
|
{
|
|
unsigned i;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
for (i = 0; i < d3d->overlays_size; i++)
|
|
d3d->overlays[i].fullscreen = enable;
|
|
}
|
|
|
|
static void d3d8_overlay_set_alpha(void *data, unsigned index, float mod)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
if (d3d)
|
|
d3d->overlays[index].alpha_mod = mod;
|
|
}
|
|
|
|
static const video_overlay_interface_t d3d8_overlay_interface = {
|
|
d3d8_overlay_enable,
|
|
d3d8_overlay_load,
|
|
d3d8_overlay_tex_geom,
|
|
d3d8_overlay_vertex_geom,
|
|
d3d8_overlay_full_screen,
|
|
d3d8_overlay_set_alpha,
|
|
};
|
|
|
|
static void d3d8_get_overlay_interface(void *data,
|
|
const video_overlay_interface_t **iface)
|
|
{
|
|
(void)data;
|
|
*iface = &d3d8_overlay_interface;
|
|
}
|
|
#endif
|
|
|
|
static void d3d8_update_title(void)
|
|
{
|
|
#ifndef _XBOX
|
|
const ui_window_t *window = ui_companion_driver_get_window_ptr();
|
|
|
|
if (window)
|
|
{
|
|
char title[128];
|
|
|
|
title[0] = '\0';
|
|
|
|
video_driver_get_window_title(title, sizeof(title));
|
|
|
|
if (title[0])
|
|
window->set_title(&main_window, title);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static bool d3d8_frame(void *data, const void *frame,
|
|
unsigned frame_width, unsigned frame_height,
|
|
uint64_t frame_count, unsigned pitch,
|
|
const char *msg, video_frame_info_t *video_info)
|
|
{
|
|
D3DVIEWPORT8 screen_vp;
|
|
unsigned i = 0;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
unsigned width = video_info->width;
|
|
unsigned height = video_info->height;
|
|
struct font_params *osd_params = (struct font_params*)
|
|
&video_info->osd_stat_params;
|
|
const char *stat_text = video_info->stat_text;
|
|
bool statistics_show = video_info->statistics_show;
|
|
unsigned black_frame_insertion = video_info->black_frame_insertion;
|
|
#ifdef HAVE_MENU
|
|
bool menu_is_alive = video_info->menu_is_alive;
|
|
#endif
|
|
|
|
if (!frame)
|
|
return true;
|
|
|
|
/* We cannot recover in fullscreen. */
|
|
if (d3d->needs_restore)
|
|
{
|
|
#ifndef _XBOX
|
|
HWND window = win32_get_window();
|
|
if (IsIconic(window))
|
|
return true;
|
|
#endif
|
|
|
|
if (!d3d8_restore(d3d))
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to restore.\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (d3d->should_resize)
|
|
{
|
|
d3d8_set_viewport(d3d, width, height, false, true);
|
|
d3d->should_resize = false;
|
|
}
|
|
|
|
/* render_chain() only clears out viewport,
|
|
* clear out everything. */
|
|
screen_vp.X = 0;
|
|
screen_vp.Y = 0;
|
|
screen_vp.MinZ = 0;
|
|
screen_vp.MaxZ = 1;
|
|
screen_vp.Width = width;
|
|
screen_vp.Height = height;
|
|
d3d8_set_viewports(d3d->dev, &screen_vp);
|
|
d3d8_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0);
|
|
|
|
|
|
if (!d3d8_renderchain_render(
|
|
d3d,
|
|
frame, frame_width, frame_height,
|
|
pitch, d3d->dev_rotation))
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to render scene.\n");
|
|
return false;
|
|
}
|
|
|
|
if (black_frame_insertion && !d3d->menu->enabled)
|
|
{
|
|
for ( int i = 0; i < video_info->black_frame_insertion; ++i)
|
|
{
|
|
if (!d3d8_swap(d3d, d3d->dev) || d3d->needs_restore)
|
|
return true;
|
|
d3d8_clear(d3d->dev, 0, 0, D3DCLEAR_TARGET, 0, 1, 0);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_MENU
|
|
if (d3d->menu && d3d->menu->enabled)
|
|
{
|
|
d3d8_set_mvp(d3d->dev, &d3d->mvp);
|
|
d3d8_overlay_render(d3d, width, height, d3d->menu, false);
|
|
|
|
d3d->menu_display.offset = 0;
|
|
d3d8_set_stream_source(d3d->dev, 0, d3d->menu_display.buffer, 0, sizeof(Vertex));
|
|
|
|
d3d8_set_viewports(d3d->dev, &screen_vp);
|
|
menu_driver_frame(menu_is_alive, video_info);
|
|
}
|
|
else if (statistics_show)
|
|
{
|
|
if (osd_params)
|
|
font_driver_render_msg(d3d, stat_text,
|
|
(const struct font_params*)osd_params, NULL);
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_OVERLAY
|
|
if (d3d->overlays_enabled)
|
|
{
|
|
d3d8_set_mvp(d3d->dev, &d3d->mvp);
|
|
for (i = 0; i < d3d->overlays_size; i++)
|
|
d3d8_overlay_render(d3d, width, height, &d3d->overlays[i], true);
|
|
}
|
|
#endif
|
|
|
|
if (!string_is_empty(msg))
|
|
{
|
|
d3d8_set_viewports(d3d->dev, &screen_vp);
|
|
d3d8_begin_scene(d3d->dev);
|
|
font_driver_render_msg(d3d, msg, NULL, NULL);
|
|
d3d8_end_scene(d3d->dev);
|
|
}
|
|
|
|
d3d8_update_title();
|
|
d3d8_swap(d3d, d3d->dev);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool d3d8_set_shader(void *data,
|
|
enum rarch_shader_type type, const char *path)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void d3d8_set_menu_texture_frame(void *data,
|
|
const void *frame, bool rgb32, unsigned width, unsigned height,
|
|
float alpha)
|
|
{
|
|
D3DLOCKED_RECT d3dlr;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
(void)d3dlr;
|
|
(void)frame;
|
|
(void)rgb32;
|
|
(void)width;
|
|
(void)height;
|
|
(void)alpha;
|
|
|
|
if ( !d3d->menu->tex ||
|
|
d3d->menu->tex_w != width ||
|
|
d3d->menu->tex_h != height)
|
|
{
|
|
if (d3d->menu)
|
|
d3d8_texture_free(d3d->menu->tex);
|
|
|
|
d3d->menu->tex = d3d8_texture_new(d3d->dev, NULL,
|
|
width, height, 1,
|
|
0, d3d8_get_argb8888_format(),
|
|
D3DPOOL_MANAGED, 0, 0, 0, NULL, NULL, false);
|
|
|
|
if (!d3d->menu->tex)
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to create menu texture.\n");
|
|
return;
|
|
}
|
|
|
|
d3d->menu->tex_w = width;
|
|
d3d->menu->tex_h = height;
|
|
#ifdef _XBOX
|
|
d3d->menu->tex_coords [2] = width;
|
|
d3d->menu->tex_coords[3] = height;
|
|
#endif
|
|
}
|
|
|
|
d3d->menu->alpha_mod = alpha;
|
|
|
|
if (d3d8_lock_rectangle(d3d->menu->tex, 0, &d3dlr,
|
|
NULL, 0, D3DLOCK_NOSYSLOCK))
|
|
{
|
|
unsigned h, w;
|
|
if (rgb32)
|
|
{
|
|
uint8_t *dst = (uint8_t*)d3dlr.pBits;
|
|
const uint32_t *src = (const uint32_t*)frame;
|
|
|
|
for (h = 0; h < height; h++, dst += d3dlr.Pitch, src += width)
|
|
{
|
|
memcpy(dst, src, width * sizeof(uint32_t));
|
|
memset(dst + width * sizeof(uint32_t), 0,
|
|
d3dlr.Pitch - width * sizeof(uint32_t));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32_t *dst = (uint32_t*)d3dlr.pBits;
|
|
const uint16_t *src = (const uint16_t*)frame;
|
|
|
|
for (h = 0; h < height; h++, dst += d3dlr.Pitch >> 2, src += width)
|
|
{
|
|
for (w = 0; w < width; w++)
|
|
{
|
|
uint16_t c = src[w];
|
|
uint32_t r = (c >> 12) & 0xf;
|
|
uint32_t g = (c >> 8) & 0xf;
|
|
uint32_t b = (c >> 4) & 0xf;
|
|
uint32_t a = (c >> 0) & 0xf;
|
|
r = ((r << 4) | r) << 16;
|
|
g = ((g << 4) | g) << 8;
|
|
b = ((b << 4) | b) << 0;
|
|
a = ((a << 4) | a) << 24;
|
|
dst[w] = r | g | b | a;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (d3d->menu)
|
|
d3d8_unlock_rectangle(d3d->menu->tex);
|
|
}
|
|
}
|
|
|
|
static void d3d8_set_menu_texture_enable(void *data,
|
|
bool state, bool full_screen)
|
|
{
|
|
d3d8_video_t *d3d = (d3d8_video_t*)data;
|
|
|
|
if (!d3d || !d3d->menu)
|
|
return;
|
|
|
|
d3d->menu->enabled = state;
|
|
d3d->menu->fullscreen = full_screen;
|
|
}
|
|
|
|
static void d3d8_video_texture_load_d3d(
|
|
struct d3d8_texture_info *info,
|
|
uintptr_t *id)
|
|
{
|
|
D3DLOCKED_RECT d3dlr;
|
|
unsigned usage = 0;
|
|
d3d8_video_t *d3d = (d3d8_video_t*)info->userdata;
|
|
struct texture_image *ti = (struct texture_image*)info->data;
|
|
LPDIRECT3DTEXTURE8 tex = (LPDIRECT3DTEXTURE8)d3d8_texture_new(d3d->dev, NULL,
|
|
ti->width, ti->height, 0,
|
|
usage, d3d8_get_argb8888_format(),
|
|
D3DPOOL_MANAGED, 0, 0, 0,
|
|
NULL, NULL, false);
|
|
|
|
if (!tex)
|
|
{
|
|
RARCH_ERR("[D3D8]: Failed to create texture\n");
|
|
return;
|
|
}
|
|
|
|
if (d3d8_lock_rectangle(tex, 0, &d3dlr,
|
|
NULL, 0, D3DLOCK_NOSYSLOCK))
|
|
{
|
|
unsigned i;
|
|
uint32_t *dst = (uint32_t*)(d3dlr.pBits);
|
|
const uint32_t *src = ti->pixels;
|
|
unsigned pitch = d3dlr.Pitch >> 2;
|
|
|
|
for (i = 0; i < ti->height; i++, dst += pitch, src += ti->width)
|
|
memcpy(dst, src, ti->width << 2);
|
|
d3d8_unlock_rectangle(tex);
|
|
}
|
|
|
|
*id = (uintptr_t)tex;
|
|
}
|
|
|
|
static int d3d8_video_texture_load_wrap_d3d(void *data)
|
|
{
|
|
uintptr_t id = 0;
|
|
struct d3d8_texture_info *info = (struct d3d8_texture_info*)data;
|
|
if (!info)
|
|
return 0;
|
|
d3d8_video_texture_load_d3d(info, &id);
|
|
return id;
|
|
}
|
|
|
|
static uintptr_t d3d8_load_texture(void *video_data, void *data,
|
|
bool threaded, enum texture_filter_type filter_type)
|
|
{
|
|
struct d3d8_texture_info info;
|
|
uintptr_t id = 0;
|
|
|
|
info.userdata = video_data;
|
|
info.data = data;
|
|
info.type = filter_type;
|
|
|
|
if (threaded)
|
|
return video_thread_texture_load(&info,
|
|
d3d8_video_texture_load_wrap_d3d);
|
|
|
|
d3d8_video_texture_load_d3d(&info, &id);
|
|
return id;
|
|
}
|
|
|
|
static void d3d8_unload_texture(void *data, bool threaded,
|
|
uintptr_t id)
|
|
{
|
|
LPDIRECT3DTEXTURE8 texid;
|
|
if (!id)
|
|
return;
|
|
|
|
texid = (LPDIRECT3DTEXTURE8)id;
|
|
d3d8_texture_free(texid);
|
|
}
|
|
|
|
static void d3d8_set_video_mode(void *data,
|
|
unsigned width, unsigned height,
|
|
bool fullscreen)
|
|
{
|
|
#ifndef _XBOX
|
|
win32_show_cursor(data, !fullscreen);
|
|
#endif
|
|
}
|
|
|
|
static uint32_t d3d8_get_flags(void *data)
|
|
{
|
|
uint32_t flags = 0;
|
|
|
|
BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION);
|
|
BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING);
|
|
|
|
return flags;
|
|
}
|
|
|
|
static const video_poke_interface_t d3d_poke_interface = {
|
|
d3d8_get_flags,
|
|
d3d8_load_texture,
|
|
d3d8_unload_texture,
|
|
d3d8_set_video_mode,
|
|
#if defined(_XBOX) || defined(__WINRT__)
|
|
NULL,
|
|
#else
|
|
/* UWP does not expose this information easily */
|
|
win32_get_refresh_rate,
|
|
#endif
|
|
NULL,
|
|
NULL, /* get_video_output_size */
|
|
NULL, /* get_video_output_prev */
|
|
NULL, /* get_video_output_next */
|
|
NULL, /* get_current_framebuffer */
|
|
NULL, /* get_proc_address */
|
|
d3d8_set_aspect_ratio,
|
|
d3d8_apply_state_changes,
|
|
d3d8_set_menu_texture_frame,
|
|
d3d8_set_menu_texture_enable,
|
|
d3d8_set_osd_msg,
|
|
|
|
win32_show_cursor,
|
|
NULL, /* grab_mouse_toggle */
|
|
NULL, /* get_current_shader */
|
|
NULL, /* get_current_software_framebuffer */
|
|
NULL /* get_hw_render_interface */
|
|
};
|
|
|
|
static void d3d8_get_poke_interface(void *data,
|
|
const video_poke_interface_t **iface)
|
|
{
|
|
*iface = &d3d_poke_interface;
|
|
}
|
|
|
|
#ifdef _XBOX
|
|
static bool d3d8_has_windowed(void *data) { return false; }
|
|
#else
|
|
static bool d3d8_has_windowed(void *data) { return true; }
|
|
#endif
|
|
|
|
video_driver_t video_d3d8 = {
|
|
d3d8_init,
|
|
d3d8_frame,
|
|
d3d8_set_nonblock_state,
|
|
d3d8_alive,
|
|
NULL, /* focus */
|
|
d3d8_suppress_screensaver,
|
|
d3d8_has_windowed,
|
|
d3d8_set_shader,
|
|
d3d8_free,
|
|
"d3d8",
|
|
d3d8_set_viewport,
|
|
d3d8_set_rotation,
|
|
d3d8_viewport_info,
|
|
NULL, /* read_viewport */
|
|
NULL, /* read_frame_raw */
|
|
#ifdef HAVE_OVERLAY
|
|
d3d8_get_overlay_interface,
|
|
#endif
|
|
#ifdef HAVE_VIDEO_LAYOUT
|
|
NULL,
|
|
#endif
|
|
d3d8_get_poke_interface
|
|
};
|