RetroArch/xdk/xdk_d3d.cpp
twinaphex a97b53f9f1 Reimplement driver.*_data_own variables so that they actually
work for a usecase now -

on Xbox, full teardown/re-initing of D3D context seems to be
error-prone - so by flagging driver.video_data_own to true inside
of the video_init function, we signal later on to the uninit_drivers
function that we DO NOT want to call the free function of this driver
and clean up the handle.

Instead, this driver should properly retain the handle by returning
the pre-existing handle when (for example) driver.video_data is
not NULL. You can see an example of this in xdk/xdk_d3d.cpp.

Overall still a quite clean solution and we will only use this in
extraordinary conditions (like this Xbox one I suppose) - full
teardown/setup will be the goal for all other platforms where we
can be certain that the state can be brought down and up entirely
during runtime without any problems.
2014-05-30 02:37:48 +02:00

958 lines
26 KiB
C++

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2014 - Daniel De Matteis
*
* 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/>.
*/
#ifdef _XBOX
#include <xtl.h>
#include <xgraphics.h>
#endif
#include "../driver.h"
#include "xdk_d3d.h"
#ifdef HAVE_HLSL
#include "../gfx/shader_hlsl.h"
#endif
#include "./../gfx/gfx_context.h"
#include "../general.h"
#include "../message_queue.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "../xdk/xdk_resources.h"
static bool d3d_init_shader(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
const gl_shader_backend_t *backend = NULL;
const char *shader_path = g_settings.video.shader_path;
enum rarch_shader_type type = gfx_shader_parse_type(shader_path, DEFAULT_SHADER_TYPE);
switch (type)
{
case RARCH_SHADER_HLSL:
#ifdef HAVE_HLSL
RARCH_LOG("[D3D]: Using HLSL shader backend.\n");
backend = &hlsl_backend;
#endif
break;
}
if (!backend)
{
RARCH_ERR("[GL]: Didn't find valid shader backend. Continuing without shaders.\n");
return true;
}
d3d->shader = backend;
return d3d->shader->init(d3d, shader_path);
}
static void d3d_free(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
if (d3d->font_ctx && d3d->font_ctx->deinit)
d3d->font_ctx->deinit(d3d);
d3d->font_ctx = NULL;
if (d3d->shader && d3d->shader->deinit)
d3d->shader->deinit();
d3d->shader = NULL;
if (d3d->ctx_driver && d3d->ctx_driver->destroy)
d3d->ctx_driver->destroy(d3d);
d3d->ctx_driver = NULL;
free(data);
}
static void d3d_set_viewport(void *data, int x, int y, unsigned width, unsigned height)
{
d3d_video_t *d3d = (d3d_video_t*)data;
D3DVIEWPORT viewport;
// D3D doesn't support negative X/Y viewports ...
if (x < 0)
x = 0;
if (y < 0)
y = 0;
viewport.Width = width;
viewport.Height = height;
viewport.X = x;
viewport.Y = y;
viewport.MinZ = 0.0f;
viewport.MaxZ = 1.0f;
d3d->final_viewport = viewport;
}
static void d3d_calculate_rect(void *data, unsigned width, unsigned height,
bool keep, float desired_aspect)
{
d3d_video_t *d3d = (d3d_video_t*)data;
LPDIRECT3DDEVICE d3dr = d3d->dev;
if (g_settings.video.scale_integer)
{
struct rarch_viewport vp = {0};
gfx_scale_integer(&vp, width, height, desired_aspect, keep);
d3d_set_viewport(d3d, vp.x, vp.y, vp.width, vp.height);
}
else if (!keep)
d3d_set_viewport(d3d, 0, 0, width, height);
else
{
if (g_settings.video.aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
{
const rarch_viewport_t &custom = g_extern.console.screen.viewports.custom_vp;
d3d_set_viewport(d3d, custom.x, custom.y, custom.width, custom.height);
}
else
{
float device_aspect = static_cast<float>(width) / static_cast<float>(height);
if (fabsf(device_aspect - desired_aspect) < 0.0001f)
d3d_set_viewport(d3d, 0, 0, width, height);
else if (device_aspect > desired_aspect)
{
float delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f;
d3d_set_viewport(d3d, int(roundf(width * (0.5f - delta))), 0, unsigned(roundf(2.0f * width * delta)), height);
}
else
{
float delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f;
d3d_set_viewport(d3d, 0, int(roundf(height * (0.5f - delta))), width, unsigned(roundf(2.0f * height * delta)));
}
}
}
}
static void d3d_set_rotation(void *data, unsigned rot)
{
(void)data;
d3d_video_t *d3d = (d3d_video_t*)data;
d3d->dev_rotation = rot;
}
static void set_mvp(void *data, unsigned vp_width, unsigned vp_height, unsigned rotation)
{
d3d_video_t *d3d = (d3d_video_t*)data;
LPDIRECT3DDEVICE d3dr = (LPDIRECT3DDEVICE)d3d->dev;
#if defined(_XBOX360) && defined(HAVE_HLSL)
hlsl_set_proj_matrix(XMMatrixRotationZ(rotation * (M_PI / 2.0)));
if (d3d->shader && d3d->shader->set_mvp)
d3d->shader->set_mvp(d3d, NULL);
#elif defined(_XBOX1)
D3DXMATRIX p_out, p_rotate, mat;
D3DXMatrixOrthoOffCenterLH(&mat, 0, vp_width, vp_height, 0, 0.0f, 1.0f);
D3DXMatrixIdentity(&p_out);
D3DXMatrixRotationZ(&p_rotate, rotation * (M_PI / 2.0));
RD3DDevice_SetTransform(d3dr, D3DTS_WORLD, &p_rotate);
RD3DDevice_SetTransform(d3dr, D3DTS_VIEW, &p_out);
RD3DDevice_SetTransform(d3dr, D3DTS_PROJECTION, &p_out);
#endif
}
static bool d3d_set_shader(void *data, enum rarch_shader_type type, const char *path)
{
/* TODO - stub */
d3d_video_t *d3d = (d3d_video_t*)data;
switch (type)
{
case RARCH_SHADER_CG:
#ifdef HAVE_HLSL
d3d->shader = &hlsl_backend;
break;
#endif
default:
d3d->shader = NULL;
break;
}
if (!d3d->shader)
{
RARCH_ERR("[D3D]: Cannot find shader core for path: %s.\n", path);
return false;
}
return true;
}
static void d3d_init_textures(void *data, const video_info_t *video)
{
HRESULT ret;
d3d_video_t *d3d = (d3d_video_t*)data;
D3DPRESENT_PARAMETERS d3dpp;
D3DVIEWPORT vp = {0};
d3d_make_d3dpp(d3d, video, &d3dpp);
d3d->pixel_size = video->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t);
if (d3d->tex)
{
d3d->tex->Release();
d3d->tex = NULL;
}
ret = d3d->dev->CreateTexture(d3d->tex_w, d3d->tex_h, 1, 0, video->rgb32 ? D3DFMT_LIN_X8R8G8B8 : D3DFMT_LIN_R5G6B5,
0, &d3d->tex
#ifdef _XBOX360
, NULL
#endif
);
if (ret != S_OK)
{
RARCH_ERR("[d3d_init_textures::] failed at CreateTexture.\n");
return;
}
D3DLOCKED_RECT d3dlr;
D3DTexture_LockRect(d3d->tex, 0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
memset(d3dlr.pBits, 0, d3d->tex_w * d3dlr.Pitch);
d3d->last_width = d3d->tex_w;
d3d->last_height = d3d->tex_h;
#ifdef _XBOX1
d3d->dev->SetRenderState(D3DRS_LIGHTING, FALSE);
#endif
vp.Width = d3d->screen_width;
vp.Height = d3d->screen_height;
d3d->dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
d3d->dev->SetRenderState(D3DRS_ZENABLE, FALSE);
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
RD3DDevice_SetViewport(d3d->dev, &vp);
if (g_extern.console.screen.viewports.custom_vp.width == 0)
g_extern.console.screen.viewports.custom_vp.width = vp.Width;
if (g_extern.console.screen.viewports.custom_vp.height == 0)
g_extern.console.screen.viewports.custom_vp.height = vp.Height;
}
static void d3d_reinit_textures(void *data, const video_info_t *video)
{
d3d_video_t *d3d = (d3d_video_t*)data;
unsigned old_base_size = d3d->pixel_size;
unsigned old_width = d3d->tex_w;
unsigned old_height = d3d->tex_h;
d3d->pixel_size = video->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t);
d3d->tex_w = d3d->tex_h = RARCH_SCALE_BASE * video->input_scale;
if (old_base_size != d3d->pixel_size || old_width != d3d->tex_w || old_height != d3d->tex_h)
{
RARCH_LOG("Reinitializing textures (%u x %u @ %u bpp)\n", d3d->tex_w,
d3d->tex_h, d3d->pixel_size * CHAR_BIT);
d3d_init_textures(d3d, video);
RARCH_LOG("Reinitializing textures skipped.\n");
}
}
static const gfx_ctx_driver_t *d3d_get_context(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
enum gfx_ctx_api api;
unsigned major, minor;
#if defined(_XBOX1)
api = GFX_CTX_DIRECT3D8_API;
major = 8;
#elif defined(_XBOX360)
api = GFX_CTX_DIRECT3D9_API;
major = 9;
#endif
minor = 0;
return gfx_ctx_init_first(d3d, api, major, minor, false);
}
static bool d3d_init_base(void *data, const video_info_t *info)
{
d3d_video_t *d3d = (d3d_video_t*)data;
D3DPRESENT_PARAMETERS d3dpp;
d3d_make_d3dpp(d3d, info, &d3dpp);
d3d->g_pD3D = direct3d_create_ctx(D3D_SDK_VERSION);
if (!d3d->g_pD3D)
{
RARCH_ERR("Failed to create D3D interface.\n");
return false;
}
if (d3d->d3d_err = d3d->g_pD3D->CreateDevice(0,
D3DDEVTYPE_HAL,
NULL,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp,
&d3d->dev) != S_OK)
{
RARCH_ERR("[D3D]: Failed to initialize device.\n");
return false;
}
return true;
}
static bool d3d_init_chain(void *data, const video_info_t *info)
{
HRESULT ret;
d3d_video_t *d3d = (d3d_video_t*)data;
LPDIRECT3DDEVICE d3dr = d3d->dev;
d3d_init_textures(d3d, info);
ret = d3d->dev->CreateVertexBuffer(4 * sizeof(DrawVerticeFormats),
D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &d3d->vertex_buf
#ifdef _XBOX360
,NULL
#endif
);
if (ret != S_OK)
{
RARCH_ERR("[d3d_init::] Failed at CreateVertexBuffer.\n");
return false;
}
#if defined(_XBOX1)
const DrawVerticeFormats init_verts[] = {
{ -1.0f, -1.0f, 1.0f, 0.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f, 1.0f },
{ -1.0f, 1.0f, 1.0f, 0.0f, 0.0f },
{ 1.0f, 1.0f, 1.0f, 1.0f, 0.0f },
};
BYTE *verts_ptr;
#elif defined(_XBOX360)
static const DrawVerticeFormats init_verts[] = {
{ -1.0f, -1.0f, 0.0f, 1.0f },
{ 1.0f, -1.0f, 1.0f, 1.0f },
{ -1.0f, 1.0f, 0.0f, 0.0f },
{ 1.0f, 1.0f, 1.0f, 0.0f },
};
void *verts_ptr;
#endif
d3d->vertex_buf->Lock(0, 0, &verts_ptr, 0);
memcpy(verts_ptr, init_verts, sizeof(init_verts));
d3d->vertex_buf->Unlock();
#if defined(_XBOX1)
d3d->dev->SetVertexShader(D3DFVF_XYZ | D3DFVF_TEX1);
#elif defined(_XBOX360)
static const D3DVERTEXELEMENT VertexElements[] =
{
{ 0, 0 * sizeof(float), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 2 * sizeof(float), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
D3DDECL_END()
};
ret = d3d->dev->CreateVertexDeclaration(VertexElements, &d3d->v_decl);
if (ret != S_OK)
{
RARCH_ERR("[d3d_init::] Failed at CreateVertexDeclaration.\n");
}
#endif
return true;
}
static bool d3d_initialize(void *data, const video_info_t *info)
{
d3d_video_t *d3d = (d3d_video_t*)data;
bool ret = true;
if (!d3d->g_pD3D)
ret = d3d_init_base(d3d, info);
else
{
}
if (!ret)
return ret;
d3d_calculate_rect(d3d, d3d->screen_width, d3d->screen_height, info->force_aspect,g_extern.system.aspect_ratio);
#ifdef HAVE_HLSL
if (!d3d_init_shader(d3d))
{
RARCH_ERR("Failed to initialize HLSL.\n");
return false;
}
#endif
if (!d3d_init_chain(d3d, info))
{
RARCH_ERR("Failed to initialize render chain.\n");
return false;
}
#if defined(_XBOX360)
strlcpy(g_settings.video.font_path, "game:\\media\\Arial_12.xpr", sizeof(g_settings.video.font_path));
#endif
d3d->font_ctx = d3d_font_init_first(d3d, g_settings.video.font_path, 0);
if (!d3d->font_ctx)
{
RARCH_ERR("Failed to initialize font.\n");
return false;
}
return true;
}
// Delay constructor due to lack of exceptions.
static bool d3d_construct(void *data, const video_info_t *info, const input_driver_t **input,
void **input_data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
d3d->should_resize = false;
unsigned full_x, full_y;
if (d3d->ctx_driver && d3d->ctx_driver->get_video_size)
d3d->ctx_driver->get_video_size(d3d, &full_x, &full_y);
d3d->screen_width = info->fullscreen ? full_x : info->width;
d3d->screen_height = info->fullscreen ? full_y : info->height;
d3d->video_info = *info;
if (!d3d_initialize(d3d, &d3d->video_info))
return false;
if (input && input_data)
d3d->ctx_driver->input_driver(d3d, input, input_data);
RARCH_LOG("[D3D]: Init complete.\n");
return true;
}
static void *d3d_init(const video_info_t *vid, const input_driver_t **input, void **input_data)
{
if (driver.video_data)
{
d3d_video_t *d3d = (d3d_video_t*)driver.video_data;
// Reinitialize textures as we might have changed pixel formats.
d3d_reinit_textures(d3d, vid);
if (input && input_data)
{
*input = driver.input;
*input_data = driver.input_data;
}
driver.video_data_own = true;
driver.input_data_own = true;
return driver.video_data;
}
d3d_video_t *d3d = (d3d_video_t*)calloc(1, sizeof(d3d_video_t));
if (!d3d)
return NULL;
d3d->ctx_driver = d3d_get_context(d3d);
if (!d3d->ctx_driver)
{
free(d3d);
return NULL;
}
//default values
d3d->g_pD3D = NULL;
d3d->dev = NULL;
d3d->dev_rotation = 0;
d3d->should_resize = false;
d3d->vsync = vid->vsync;
d3d->tex_w = RARCH_SCALE_BASE * vid->input_scale;
d3d->tex_h = RARCH_SCALE_BASE * vid->input_scale;
if (!d3d_construct(d3d, vid, input, input_data))
{
RARCH_ERR("[D3D]: Failed to init D3D.\n");
delete d3d;
return NULL;
}
#ifdef _XBOX
driver.video_data_own = true;
driver.input_data_own = true;
#endif
return d3d;
}
#ifdef HAVE_RMENU
extern struct texture_image *menu_texture;
#endif
#ifdef _XBOX1
static bool texture_image_render(void *data, struct texture_image *out_img,
int x, int y, int w, int h, bool force_fullscreen)
{
d3d_video_t *d3d = (d3d_video_t*)data;
LPDIRECT3DDEVICE d3dr = (LPDIRECT3DDEVICE)d3d->dev;
if (out_img->pixels == NULL || out_img->vertex_buf == NULL)
return false;
float fX = static_cast<float>(x);
float fY = static_cast<float>(y);
// create the new vertices
DrawVerticeFormats newVerts[] =
{
// x, y, z, color, u ,v
{fX, fY, 0.0f, 0, 0, 0},
{fX + w, fY, 0.0f, 0, 1, 0},
{fX + w, fY + h, 0.0f, 0, 1, 1},
{fX, fY + h, 0.0f, 0, 0, 1}
};
// load the existing vertices
DrawVerticeFormats *pCurVerts;
HRESULT ret = out_img->vertex_buf->Lock(0, 0, (unsigned char**)&pCurVerts, 0);
if (FAILED(ret))
{
RARCH_ERR("Error occurred during m_pVertexBuffer->Lock().\n");
return false;
}
// copy the new verts over the old verts
memcpy(pCurVerts, newVerts, 4 * sizeof(DrawVerticeFormats));
out_img->vertex_buf->Unlock();
d3d->dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
d3d->dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
d3d->dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
// also blend the texture with the set alpha value
d3d->dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
d3d->dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
d3d->dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
// draw the quad
d3dr->SetTexture(0, out_img->pixels);
d3dr->SetStreamSource(0, out_img->vertex_buf, sizeof(DrawVerticeFormats));
d3dr->SetVertexShader(D3DFVF_CUSTOMVERTEX);
if (force_fullscreen)
{
D3DVIEWPORT vp = {0};
vp.Width = w;
vp.Height = h;
vp.X = 0;
vp.Y = 0;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
d3dr->SetViewport(&vp);
}
d3dr->DrawPrimitive(D3DPT_QUADLIST, 0, 1);
return true;
}
#endif
#ifdef HAVE_MENU
static void d3d_draw_texture(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
#if defined(HAVE_RMENU)
menu_texture->x = 0;
menu_texture->y = 0;
if (d3d->rgui_texture_enable)
{
d3d->dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
d3d->dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
d3d->dev->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
texture_image_render(d3d, menu_texture, menu_texture->x, menu_texture->y,
d3d->screen_width, d3d->screen_height, true);
d3d->dev->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}
#endif
}
#endif
static void clear_texture(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
LPDIRECT3DDEVICE d3dr = d3d->dev;
D3DLOCKED_RECT d3dlr;
D3DTexture_LockRect(d3d->tex, 0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
memset(d3dlr.pBits, 0, d3d->tex_w * d3dlr.Pitch);
}
static void blit_to_texture(void *data, const void *frame,
unsigned width, unsigned height, unsigned pitch)
{
d3d_video_t *d3d = (d3d_video_t*)data;
if (d3d->last_width != width || d3d->last_height != height)
clear_texture(data);
D3DLOCKED_RECT d3dlr;
D3DTexture_LockRect(d3d->tex, 0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
#if defined(_XBOX360)
D3DSURFACE_DESC desc;
d3d->tex->GetLevelDesc(0, &desc);
XGCopySurface(d3dlr.pBits, d3dlr.Pitch, width, height, desc.Format, NULL, frame,
pitch, desc.Format, NULL, 0, 0);
#elif defined(_XBOX1)
for (unsigned y = 0; y < height; y++)
{
const uint8_t *in = (const uint8_t*)frame + y * pitch;
uint8_t *out = (uint8_t*)d3dlr.pBits + y * d3dlr.Pitch;
memcpy(out, in, width * d3d->pixel_size);
}
#endif
}
static void set_vertices(void *data, unsigned pass, unsigned width, unsigned height)
{
d3d_video_t *d3d = (d3d_video_t*)data;
if (d3d->last_width != width || d3d->last_height != height)
{
#if defined(_XBOX1)
float tex_w = width;
float tex_h = height;
DrawVerticeFormats verts[] = {
{ -1.0f, -1.0f, 1.0f, 0.0f, tex_h },
{ 1.0f, -1.0f, 1.0f, tex_w, tex_h },
{ -1.0f, 1.0f, 1.0f, 0.0f, 0.0f },
{ 1.0f, 1.0f, 1.0f, tex_w, 0.0f },
};
#elif defined(_XBOX360)
float tex_w = width / ((float)d3d->tex_w);
float tex_h = height / ((float)d3d->tex_h);
DrawVerticeFormats verts[] = {
{ -1.0f, -1.0f, 0.0f, tex_h },
{ 1.0f, -1.0f, tex_w, tex_h },
{ -1.0f, 1.0f, 0.0f, 0.0f },
{ 1.0f, 1.0f, tex_w, 0.0f },
};
#endif
// Align texels and vertices (D3D9 quirk).
for (unsigned i = 0; i < 4; i++)
{
verts[i].x -= 0.5f / ((float)d3d->tex_w);
verts[i].y += 0.5f / ((float)d3d->tex_h);
}
#if defined(_XBOX1)
BYTE *verts_ptr;
#elif defined(_XBOX360)
void *verts_ptr;
#endif
RD3DVertexBuffer_Lock(d3d->vertex_buf, 0, 0, &verts_ptr, 0);
memcpy(verts_ptr, verts, sizeof(verts));
RD3DVertexBuffer_Unlock(d3d->vertex_buf);
d3d->last_width = width;
d3d->last_height = height;
}
if (d3d->shader)
{
set_mvp(d3d, d3d->screen_width, d3d->screen_height, d3d->dev_rotation);
if (d3d->shader->use)
d3d->shader->use(d3d, pass);
if (d3d->shader->set_params)
d3d->shader->set_params(d3d, width, height, d3d->tex_w, d3d->tex_h, d3d->screen_width,
d3d->screen_height, g_extern.frame_count,
NULL, NULL, NULL, 0);
}
}
static void render_pass(void *data, const void *frame, unsigned width, unsigned height,
unsigned pitch, unsigned rotation)
{
d3d_video_t *d3d = (d3d_video_t*)data;
LPDIRECT3DDEVICE d3dr = (LPDIRECT3DDEVICE)d3d->dev;
#ifndef _XBOX1
DWORD fetchConstant;
UINT64 pendingMask3;
#endif
#ifdef _XBOX1
d3dr->SetFlickerFilter(g_extern.console.screen.flicker_filter_index);
d3dr->SetSoftDisplayFilter(g_extern.lifecycle_state & (1ULL << MODE_VIDEO_SOFT_FILTER_ENABLE));
#endif
blit_to_texture(d3d, frame, width, height, pitch);
set_vertices(d3d, 1, width, height);
if (g_extern.frame_count)
#ifdef _XBOX1
d3dr->SwitchTexture(0, d3d->tex);
#elif defined _XBOX360
d3dr->SetTextureFetchConstant(0, d3d->tex);
#endif
else if (d3d->tex)
RD3DDevice_SetTexture(d3dr, 0, d3d->tex);
RD3DDevice_SetViewport(d3d->dev, &d3d->final_viewport);
RD3DDevice_SetSamplerState_MinFilter(d3dr, 0, g_settings.video.smooth ? D3DTEXF_LINEAR : D3DTEXF_POINT);
RD3DDevice_SetSamplerState_MagFilter(d3dr, 0, g_settings.video.smooth ? D3DTEXF_LINEAR : D3DTEXF_POINT);
RD3DDevice_SetSamplerState_AddressU(d3dr, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
RD3DDevice_SetSamplerState_AddressV(d3dr, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
#if defined(_XBOX1)
RD3DDevice_SetVertexShader(d3dr, D3DFVF_XYZ | D3DFVF_TEX1);
IDirect3DDevice8_SetStreamSource(d3dr, 0, d3d->vertex_buf, sizeof(DrawVerticeFormats));
#elif defined(_XBOX360)
D3DDevice_SetVertexDeclaration(d3dr, d3d->v_decl);
D3DDevice_SetStreamSource_Inline(d3dr, 0, d3d->vertex_buf, 0, sizeof(DrawVerticeFormats));
#endif
d3dr->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
g_extern.frame_count++;
set_mvp(d3d, d3d->screen_width, d3d->screen_height, d3d->dev_rotation);
}
static bool d3d_frame(void *data, const void *frame,
unsigned width, unsigned height, unsigned pitch, const char *msg)
{
d3d_video_t *d3d = (d3d_video_t*)data;
LPDIRECT3DDEVICE d3dr = d3d->dev;
if (!frame)
return true;
if (d3d->should_resize)
{
d3d_calculate_rect(d3d, d3d->screen_width, d3d->screen_height, d3d->video_info.force_aspect, g_extern.system.aspect_ratio);
d3d->should_resize = false;
}
// render_chain() only clears out viewport, clear out everything
D3DVIEWPORT screen_vp;
screen_vp.X = 0;
screen_vp.Y = 0;
screen_vp.MinZ = 0;
screen_vp.MaxZ = 1;
screen_vp.Width = d3d->screen_width;
screen_vp.Height = d3d->screen_height;
d3dr->SetViewport(&screen_vp);
d3dr->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0);
// Insert black frame first, so we can screenshot, etc.
if (g_settings.video.black_frame_insertion)
{
d3dr->Present(NULL, NULL, NULL, NULL);
d3dr->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0);
}
render_pass(d3d, frame, width, height, pitch, d3d->dev_rotation);
#ifdef HAVE_MENU
if (g_extern.lifecycle_state & (1ULL << MODE_MENU) && driver.menu_ctx && driver.menu_ctx->frame)
driver.menu_ctx->frame(d3d);
if (d3d && d3d->rgui_texture_enable)
d3d_draw_texture(d3d);
#endif
if (d3d && d3d->ctx_driver && d3d->ctx_driver->update_window_title)
d3d->ctx_driver->update_window_title(d3d);
if (msg)
{
#if defined(_XBOX1)
float msg_width = 60;
float msg_height = 365;
#elif defined(_XBOX360)
float msg_width = (g_extern.lifecycle_state & (1ULL << MODE_MENU_HD)) ? 160 : 100;
float msg_height = 90;
#endif
font_params_t font_parms = {0};
font_parms.x = msg_width;
font_parms.y = msg_height;
font_parms.scale = 21;
d3d->font_ctx->render_msg(d3d, msg, &font_parms);
}
if (d3d && d3d->ctx_driver && d3d->ctx_driver->swap_buffers)
d3d->ctx_driver->swap_buffers(d3d);
return true;
}
static void d3d_set_nonblock_state(void *data, bool state)
{
d3d_video_t *d3d = (d3d_video_t*)data;
d3d->video_info.vsync = !state;
if (d3d->ctx_driver && d3d->ctx_driver->swap_interval)
d3d->ctx_driver->swap_interval(d3d, state ? 0 : 1);
}
static bool d3d_alive(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
bool quit, resize;
if (d3d->ctx_driver && d3d->ctx_driver->check_window)
d3d->ctx_driver->check_window(d3d, &quit,
&resize, NULL, NULL, g_extern.frame_count);
if (quit)
d3d->quitting = true;
else if (resize)
d3d->should_resize = true;
return !d3d->quitting;
}
static bool d3d_focus(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
return d3d->ctx_driver->has_focus(d3d);
}
static void d3d_set_aspect_ratio(void *data, unsigned aspect_ratio_idx)
{
d3d_video_t *d3d = (d3d_video_t*)data;
if (aspect_ratio_idx == ASPECT_RATIO_SQUARE)
gfx_set_square_pixel_viewport(g_extern.system.av_info.geometry.base_width, g_extern.system.av_info.geometry.base_height);
else if (aspect_ratio_idx == ASPECT_RATIO_CORE)
gfx_set_core_viewport();
else if (aspect_ratio_idx == ASPECT_RATIO_CONFIG)
gfx_set_config_viewport();
g_extern.system.aspect_ratio = aspectratio_lut[aspect_ratio_idx].value;
d3d->video_info.force_aspect = true;
d3d->should_resize = true;
}
static void d3d_set_filtering(void *data, unsigned index, bool set_smooth) { }
static void d3d_apply_state_changes(void *data)
{
d3d_video_t *d3d = (d3d_video_t*)data;
d3d->should_resize = true;
}
#ifdef HAVE_MENU
static void d3d_set_texture_frame(void *data,
const void *frame, bool rgb32, unsigned width, unsigned height,
float alpha)
{
(void)frame;
(void)rgb32;
(void)width;
(void)height;
(void)alpha;
}
static void d3d_set_texture_enable(void *data, bool state, bool full_screen)
{
d3d_video_t *d3d = (d3d_video_t*)data;
d3d->rgui_texture_enable = state;
d3d->rgui_texture_full_screen = full_screen;
}
#endif
static void d3d_set_osd_msg(void *data, const char *msg, void *userdata)
{
d3d_video_t *d3d = (d3d_video_t*)data;
font_params_t *params = (font_params_t*)userdata;
if (d3d->font_ctx && d3d->font_ctx->render_msg)
d3d->font_ctx->render_msg(d3d, msg, params);
}
static const video_poke_interface_t d3d_poke_interface = {
d3d_set_filtering,
#ifdef HAVE_FBO
NULL,
NULL,
#endif
d3d_set_aspect_ratio,
d3d_apply_state_changes,
#ifdef HAVE_MENU
d3d_set_texture_frame,
d3d_set_texture_enable,
#endif
d3d_set_osd_msg,
};
static void d3d_get_poke_interface(void *data, const video_poke_interface_t **iface)
{
(void)data;
*iface = &d3d_poke_interface;
}
static void d3d_restart(void)
{
d3d_video_t *d3d = (d3d_video_t*)driver.video_data;
LPDIRECT3DDEVICE d3dr = d3d->dev;
if (!d3d)
return;
D3DPRESENT_PARAMETERS d3dpp;
video_info_t video_info = {0};
video_info.vsync = g_settings.video.vsync;
video_info.force_aspect = false;
video_info.smooth = g_settings.video.smooth;
video_info.input_scale = 2;
video_info.fullscreen = true;
video_info.rgb32 = (d3d->pixel_size == sizeof(uint32_t)) ? true : false;
d3d_make_d3dpp(d3d, &video_info, &d3dpp);
d3dr->Reset(&d3dpp);
}
const video_driver_t video_d3d = {
d3d_init,
d3d_frame,
d3d_set_nonblock_state,
d3d_alive,
d3d_focus,
d3d_set_shader,
d3d_free,
"d3d",
d3d_restart,
d3d_set_rotation,
NULL, /* viewport_info */
NULL, /* read_viewport */
#ifdef HAVE_OVERLAY
NULL, /* overlay_interface */
#endif
d3d_get_poke_interface,
};