2492 lines
83 KiB
C++
2492 lines
83 KiB
C++
// gdraw_d3d1x_shared.inl - author: Fabian Giesen - copyright 2012 RAD Game Tools
|
|
//
|
|
// This file implements the part of the Iggy graphics driver layer shared between
|
|
// D3D10 and 11 (which is most of it). It heavily depends on a bunch of typedefs,
|
|
// #defines and utility functions that need to be set up correctly for the D3D version
|
|
// being targeted. This is a bit ugly, but much easier to maintain than the original
|
|
// solution, where we just kept two almost identical versions of this code.
|
|
|
|
// That native handle type holds resource handles and a coarse description.
|
|
typedef union {
|
|
// handle that is a texture
|
|
struct {
|
|
ID3D1X(Texture2D) *d3d;
|
|
ID3D1X(ShaderResourceView) *d3d_view;
|
|
ID3D1X(RenderTargetView) *d3d_rtview;
|
|
U32 w, h;
|
|
} tex;
|
|
|
|
// handle that is a vertex buffer
|
|
struct {
|
|
ID3D1X(Buffer) *verts;
|
|
ID3D1X(Buffer) *inds;
|
|
} vbuf;
|
|
} GDrawNativeHandle;
|
|
|
|
#define GDRAW_NO_STREAMING_MIPGEN // This renderer doesn't use GDraw-internal mipmap generation
|
|
#include "gdraw_shared.inl"
|
|
|
|
// max rendertarget stack depth. this depends on the extent to which you
|
|
// use filters and non-standard blend modes, and how nested they are.
|
|
#define MAX_RENDER_STACK_DEPTH 8 // Iggy is hardcoded to a limit of 16... probably 1-3 is realistic
|
|
#define AATEX_SAMPLER 7 // sampler that aa_tex gets set in
|
|
#define STENCIL_STATE_CACHE_SIZE 32 // number of distinct stencil states we cache DepthStencilStates for
|
|
#define QUAD_IB_COUNT 2048 // quad index buffer has indices for this many quads
|
|
|
|
#define ASSERT_COUNT(a,b) ((a) == (b) ? (b) : -1)
|
|
|
|
static GDrawFunctions gdraw_funcs;
|
|
|
|
// render target state
|
|
typedef struct
|
|
{
|
|
GDrawHandle *color_buffer;
|
|
S32 base_x, base_y, width, height;
|
|
U32 flags;
|
|
rrbool cached;
|
|
} GDrawFramebufferState;
|
|
|
|
struct ProgramWithCachedVariableLocations
|
|
{
|
|
DWORD *bytecode;
|
|
union {
|
|
DWORD size;
|
|
ID3D1X(PixelShader) *pshader;
|
|
ID3D1X(VertexShader) *vshader;
|
|
};
|
|
};
|
|
|
|
struct DynBuffer
|
|
{
|
|
ID3D1X(Buffer) *buffer;
|
|
U32 size; // size of buffer
|
|
U32 write_pos; // start of most recently allocated chunk
|
|
U32 alloc_pos; // end of most recently allocated chunk (=start of next allocation)
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GDraw data structure
|
|
//
|
|
//
|
|
// This is the primary rendering abstraction, which hides all
|
|
// the platform-specific rendering behavior from Iggy. It is
|
|
// full of platform-specific graphics state, and also general
|
|
// graphics state so that it doesn't have to callback into Iggy
|
|
// to get at that graphics state.
|
|
|
|
typedef struct
|
|
{
|
|
ID3D1XDevice *d3d_device;
|
|
ID3D1XContext *d3d_context;
|
|
|
|
// fragment shaders
|
|
ProgramWithCachedVariableLocations fprog[GDRAW_TEXTURE__count][3];
|
|
ProgramWithCachedVariableLocations exceptional_blend[GDRAW_BLENDSPECIAL__count];
|
|
ProgramWithCachedVariableLocations filter_prog[2][16];
|
|
ProgramWithCachedVariableLocations blur_prog[MAX_TAPS+1];
|
|
ProgramWithCachedVariableLocations colormatrix;
|
|
ProgramWithCachedVariableLocations clear_ps;
|
|
|
|
// vertex input layouts
|
|
ID3D1X(InputLayout) *inlayout[GDRAW_vformat__count];
|
|
|
|
// vertex shaders
|
|
ProgramWithCachedVariableLocations vert[GDRAW_vformat__count]; // [format]
|
|
|
|
// render targets
|
|
GDrawHandleCache rendertargets;
|
|
GDrawHandle rendertarget_handles[MAX_RENDER_STACK_DEPTH]; // not -1, because we use +1 to initialize
|
|
|
|
gswf_recti rt_valid[MAX_RENDER_STACK_DEPTH+1]; // valid rect for texture clamping
|
|
|
|
// size of framebuffer-sized texture used for implementing blend modes
|
|
S32 frametex_width, frametex_height;
|
|
|
|
// viewport setting (in pixels) for current frame
|
|
S32 vx,vy;
|
|
S32 fw,fh; // full width/height of virtual display
|
|
S32 tw,th; // actual width/height of current tile
|
|
S32 tpw,tph; // width/height of padded version of tile
|
|
|
|
S32 tx0,ty0;
|
|
S32 tx0p,ty0p;
|
|
rrbool in_blur;
|
|
struct {
|
|
S32 x,y,w,h;
|
|
} cview; // current viewport
|
|
|
|
F32 projection[4]; // scalex,scaley,transx,transy
|
|
F32 projmat[3][4];
|
|
F32 xform_3d[3][4];
|
|
rrbool use_3d;
|
|
|
|
ID3D1X(RenderTargetView) *main_framebuffer;
|
|
ID3D1X(DepthStencilView) *depth_buffer[2]; // 0=main, 1=rendertarget
|
|
ID3D1X(ShaderResourceView) *main_resolve_target;
|
|
rrbool main_msaa; // does main framebuffer have MSAA enabled?
|
|
|
|
ID3D1X(Texture2D) *rt_depth_buffer;
|
|
ID3D1X(Texture2D) *aa_tex;
|
|
ID3D1X(ShaderResourceView) *aa_tex_view;
|
|
ID3D1X(Buffer) *quad_ib; // canned quad indices
|
|
|
|
// scale factor converting worldspace to viewspace <0,0>..<w,h>
|
|
F32 world_to_pixel[2];
|
|
|
|
// state objects
|
|
ID3D1X(RasterizerState) *raster_state[2]; // [msaa]
|
|
ID3D1X(SamplerState) *sampler_state[2][GDRAW_WRAP__count]; // [nearest][wrap]
|
|
ID3D1X(BlendState) *blend_state[GDRAW_BLEND__count];
|
|
ID3D1X(BlendState) *blend_no_color_write;
|
|
ID3D1X(DepthStencilState) *depth_state[2][2]; // [set_id][test_id]
|
|
|
|
// stencil state cache
|
|
// SOA so the keys are tightly packed in a few cache lines!
|
|
U32 stencil_cache_key[STENCIL_STATE_CACHE_SIZE];
|
|
ID3D1X(DepthStencilState) *stencil_cache[STENCIL_STATE_CACHE_SIZE];
|
|
U32 stencil_cache_lru[STENCIL_STATE_CACHE_SIZE];
|
|
U32 stencil_cache_now;
|
|
|
|
// constant buffers
|
|
ID3D1X(Buffer) *cb_vertex;
|
|
ID3D1X(Buffer) *cb_ps_common;
|
|
ID3D1X(Buffer) *cb_filter;
|
|
ID3D1X(Buffer) *cb_colormatrix;
|
|
ID3D1X(Buffer) *cb_blur;
|
|
|
|
// streaming buffers for dynamic vertex/index data
|
|
DynBuffer dyn_vb;
|
|
DynBuffer dyn_ib;
|
|
|
|
U32 dyn_maxalloc, last_dyn_maxalloc;
|
|
S32 max_quad_vert_count;
|
|
|
|
// cached state
|
|
U32 scissor_state; // ~0 if unknown, otherwise 0 or 1
|
|
S32 blend_mode; // -1 if unknown, otherwise GDRAW_BLEND_*
|
|
|
|
// render-state stack described above for 'temporary' rendering
|
|
GDrawFramebufferState frame[MAX_RENDER_STACK_DEPTH];
|
|
GDrawFramebufferState *cur;
|
|
|
|
// texture and vertex buffer pools
|
|
GDrawHandleCache *texturecache;
|
|
GDrawHandleCache *vbufcache;
|
|
|
|
// stat tracking
|
|
rrbool frame_done;
|
|
U64 frame_counter;
|
|
|
|
// error handler
|
|
void (__cdecl *error_handler)(HRESULT hr);
|
|
} GDraw;
|
|
|
|
static GDraw *gdraw;
|
|
|
|
static const F32 four_zeros[4] = { 0 }; // used in several places
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// General resource management for both textures and vertex buffers
|
|
//
|
|
|
|
template<typename T>
|
|
static void safe_release(T *&p)
|
|
{
|
|
if (p) {
|
|
p->Release();
|
|
p = NULL;
|
|
}
|
|
}
|
|
|
|
static void report_d3d_error(HRESULT hr, char *call, char *context)
|
|
{
|
|
if (hr == E_OUTOFMEMORY)
|
|
IggyGDrawSendWarning(NULL, "GDraw D3D out of memory in %s%s", call, context);
|
|
else
|
|
IggyGDrawSendWarning(NULL, "GDraw D3D error in %s%s: 0x%08x", call, context, hr);
|
|
}
|
|
|
|
static void unbind_resources(void)
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
|
|
// unset active textures and vertex/index buffers,
|
|
// to make sure there are no dangling refs
|
|
static ID3D1X(ShaderResourceView) *no_views[3] = { 0 };
|
|
ID3D1X(Buffer) *no_vb = NULL;
|
|
UINT no_offs = 0;
|
|
|
|
d3d->PSSetShaderResources(0, 3, no_views);
|
|
d3d->IASetVertexBuffers(0, 1, &no_vb, &no_offs, &no_offs);
|
|
d3d->IASetIndexBuffer(NULL, DXGI_FORMAT_UNKNOWN, 0);
|
|
}
|
|
|
|
static void api_free_resource(GDrawHandle *r)
|
|
{
|
|
unbind_resources();
|
|
if (r->state != GDRAW_HANDLE_STATE_user_owned) {
|
|
if (!r->cache->is_vertex) {
|
|
safe_release(r->handle.tex.d3d_view);
|
|
safe_release(r->handle.tex.d3d_rtview);
|
|
safe_release(r->handle.tex.d3d);
|
|
} else {
|
|
safe_release(r->handle.vbuf.verts);
|
|
safe_release(r->handle.vbuf.inds);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void RADLINK gdraw_UnlockHandles(GDrawStats * /*stats*/)
|
|
{
|
|
gdraw_HandleCacheUnlockAll(gdraw->texturecache);
|
|
gdraw_HandleCacheUnlockAll(gdraw->vbufcache);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Dynamic buffer
|
|
//
|
|
|
|
static void *start_write_dyn(DynBuffer *buf, U32 size)
|
|
{
|
|
U8 *ptr = NULL;
|
|
|
|
if (size > buf->size) {
|
|
IggyGDrawSendWarning(NULL, "GDraw dynamic vertex buffer usage of %d bytes in one call larger than buffer size %d", size, buf->size);
|
|
return NULL;
|
|
}
|
|
|
|
// update statistics
|
|
gdraw->dyn_maxalloc = RR_MAX(gdraw->dyn_maxalloc, size);
|
|
|
|
// invariant: current alloc_pos is in [0,size]
|
|
assert(buf->alloc_pos <= buf->size);
|
|
|
|
// wrap around when less than "size" bytes left in buffer
|
|
buf->write_pos = ((buf->size - buf->alloc_pos) < size) ? 0 : buf->alloc_pos;
|
|
|
|
// discard buffer whenever the current write position is 0;
|
|
// done this way so that if a DISCARD Map() were to fail, we would
|
|
// just keep retrying the next time around.
|
|
ptr = (U8 *) map_buffer(gdraw->d3d_context, buf->buffer, buf->write_pos == 0);
|
|
if (ptr) {
|
|
ptr += buf->write_pos; // we return pointer to write position in buffer
|
|
buf->alloc_pos = buf->write_pos + size; // bump alloc position
|
|
assert(buf->alloc_pos <= buf->size); // invariant again
|
|
}
|
|
// if map_buffer fails, it will have sent a warning
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static U32 end_write_dyn(DynBuffer *buf)
|
|
{
|
|
unmap_buffer(gdraw->d3d_context, buf->buffer);
|
|
return buf->write_pos;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Stencil state cache
|
|
//
|
|
|
|
static void stencil_state_cache_clear()
|
|
{
|
|
S32 i;
|
|
|
|
for (i=0; i < STENCIL_STATE_CACHE_SIZE; ++i) {
|
|
gdraw->stencil_cache_key[i] = 0;
|
|
safe_release(gdraw->stencil_cache[i]);
|
|
gdraw->stencil_cache_lru[i] = 0;
|
|
}
|
|
|
|
gdraw->stencil_cache_now = 0;
|
|
}
|
|
|
|
static ID3D1X(DepthStencilState) *stencil_state_cache_lookup(rrbool set_id, rrbool test_id, U8 read_mask, U8 write_mask)
|
|
{
|
|
D3D1X_(DEPTH_STENCIL_DESC) desc;
|
|
S32 i, best = 0;
|
|
U32 key = (set_id << 1) | test_id | (read_mask << 8) | (write_mask << 16);
|
|
U32 now, age, highest_age;
|
|
HRESULT hr;
|
|
|
|
// for LRU
|
|
now = ++gdraw->stencil_cache_now;
|
|
|
|
// do we have this in the cache?
|
|
for (i=0; i < STENCIL_STATE_CACHE_SIZE; ++i) {
|
|
if (gdraw->stencil_cache_key[i] == key) {
|
|
gdraw->stencil_cache_lru[i] = now;
|
|
return gdraw->stencil_cache[i];
|
|
}
|
|
}
|
|
|
|
// not in the cache, find the best slot to replace it with (LRU)
|
|
highest_age = 0;
|
|
for (i=0; i < STENCIL_STATE_CACHE_SIZE; ++i) {
|
|
if (!gdraw->stencil_cache[i]) { // unused slot!
|
|
best = i;
|
|
break;
|
|
}
|
|
|
|
age = now - gdraw->stencil_cache_lru[i];
|
|
if (age > highest_age) {
|
|
highest_age = age;
|
|
best = i;
|
|
}
|
|
}
|
|
|
|
// release old depth/stencil state at that position and create new one
|
|
safe_release(gdraw->stencil_cache[best]);
|
|
|
|
gdraw->depth_state[set_id][test_id]->GetDesc(&desc); // reference state
|
|
desc.StencilEnable = TRUE;
|
|
desc.StencilReadMask = read_mask;
|
|
desc.StencilWriteMask = write_mask;
|
|
desc.FrontFace.StencilFailOp = D3D1X_(STENCIL_OP_KEEP);
|
|
desc.FrontFace.StencilDepthFailOp = D3D1X_(STENCIL_OP_KEEP);
|
|
desc.FrontFace.StencilPassOp = D3D1X_(STENCIL_OP_REPLACE);
|
|
desc.FrontFace.StencilFunc = D3D1X_(COMPARISON_EQUAL);
|
|
desc.BackFace.StencilFailOp = D3D1X_(STENCIL_OP_KEEP);
|
|
desc.BackFace.StencilDepthFailOp = D3D1X_(STENCIL_OP_KEEP);
|
|
desc.BackFace.StencilPassOp = D3D1X_(STENCIL_OP_REPLACE);
|
|
desc.BackFace.StencilFunc = D3D1X_(COMPARISON_EQUAL);
|
|
|
|
hr = gdraw->d3d_device->CreateDepthStencilState(&desc, &gdraw->stencil_cache[best]);
|
|
if (FAILED(hr))
|
|
report_d3d_error(hr, "CreateDepthStencilState", "");
|
|
|
|
gdraw->stencil_cache_key[best] = key;
|
|
gdraw->stencil_cache_lru[best] = now;
|
|
return gdraw->stencil_cache[best];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Texture creation/updating/deletion
|
|
//
|
|
|
|
extern GDrawTexture *gdraw_D3D1X_(WrappedTextureCreate)(ID3D1X(ShaderResourceView) *tex_view)
|
|
{
|
|
GDrawStats stats={0};
|
|
GDrawHandle *p = gdraw_res_alloc_begin(gdraw->texturecache, 0, &stats); // it may need to free one item to give us a handle
|
|
p->handle.tex.d3d = NULL;
|
|
p->handle.tex.d3d_view = tex_view;
|
|
p->handle.tex.d3d_rtview = NULL;
|
|
p->handle.tex.w = 1;
|
|
p->handle.tex.h = 1;
|
|
gdraw_HandleCacheAllocateEnd(p, 0, NULL, GDRAW_HANDLE_STATE_user_owned);
|
|
return (GDrawTexture *) p;
|
|
}
|
|
|
|
extern void gdraw_D3D1X_(WrappedTextureChange)(GDrawTexture *tex, ID3D1X(ShaderResourceView) *tex_view)
|
|
{
|
|
GDrawHandle *p = (GDrawHandle *) tex;
|
|
p->handle.tex.d3d = NULL;
|
|
p->handle.tex.d3d_view = tex_view;
|
|
}
|
|
|
|
extern void gdraw_D3D1X_(WrappedTextureDestroy)(GDrawTexture *tex)
|
|
{
|
|
GDrawStats stats={0};
|
|
gdraw_res_free((GDrawHandle *) tex, &stats);
|
|
}
|
|
|
|
static void RADLINK gdraw_SetTextureUniqueID(GDrawTexture *tex, void *old_id, void *new_id)
|
|
{
|
|
GDrawHandle *p = (GDrawHandle *) tex;
|
|
// if this is still the handle it's thought to be, change the owner;
|
|
// if the owner *doesn't* match, then they're changing a stale handle, so ignore
|
|
if (p->owner == old_id)
|
|
p->owner = new_id;
|
|
}
|
|
|
|
|
|
static rrbool RADLINK gdraw_MakeTextureBegin(void *owner, S32 width, S32 height, gdraw_texture_format format, U32 flags, GDraw_MakeTexture_ProcessingInfo *p, GDrawStats *stats)
|
|
{
|
|
GDrawHandle *t = NULL;
|
|
DXGI_FORMAT dxgi_fmt;
|
|
S32 bpp, size = 0, nmips = 0;
|
|
|
|
if (width >= 16384 || height >= 16384) {
|
|
IggyGDrawSendWarning(NULL, "GDraw texture size too large (%d x %d), dimension limit is 16384", width, height);
|
|
return false;
|
|
}
|
|
|
|
if (format == GDRAW_TEXTURE_FORMAT_rgba32) {
|
|
dxgi_fmt = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
bpp = 4;
|
|
} else {
|
|
dxgi_fmt = DXGI_FORMAT_R8_UNORM;
|
|
bpp = 1;
|
|
}
|
|
|
|
// compute estimated size of texture in video memory
|
|
do {
|
|
size += RR_MAX(width >> nmips, 1) * RR_MAX(height >> nmips, 1) * bpp;
|
|
++nmips;
|
|
} while ((flags & GDRAW_MAKETEXTURE_FLAGS_mipmap) && ((width >> nmips) || (height >> nmips)));
|
|
|
|
// try to allocate memory for the client to write to
|
|
p->texture_data = (U8 *) IggyGDrawMalloc(size);
|
|
if (!p->texture_data) {
|
|
IggyGDrawSendWarning(NULL, "GDraw out of memory to store texture data to pass to D3D for %d x %d texture", width, height);
|
|
return false;
|
|
}
|
|
|
|
// allocate a handle and make room in the cache for this much data
|
|
t = gdraw_res_alloc_begin(gdraw->texturecache, size, stats);
|
|
if (!t) {
|
|
IggyGDrawFree(p->texture_data);
|
|
return false;
|
|
}
|
|
|
|
t->handle.tex.w = width;
|
|
t->handle.tex.h = height;
|
|
t->handle.tex.d3d = NULL;
|
|
t->handle.tex.d3d_view = NULL;
|
|
t->handle.tex.d3d_rtview = NULL;
|
|
|
|
p->texture_type = GDRAW_TEXTURE_TYPE_rgba;
|
|
p->p0 = t;
|
|
p->p1 = owner;
|
|
p->i0 = width;
|
|
p->i1 = height;
|
|
p->i2 = flags;
|
|
p->i3 = dxgi_fmt;
|
|
p->i4 = size;
|
|
p->i5 = nmips;
|
|
p->i6 = bpp;
|
|
|
|
p->stride_in_bytes = width * bpp;
|
|
p->num_rows = height;
|
|
|
|
return true;
|
|
}
|
|
|
|
static rrbool RADLINK gdraw_MakeTextureMore(GDraw_MakeTexture_ProcessingInfo * /*p*/)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static GDrawTexture * RADLINK gdraw_MakeTextureEnd(GDraw_MakeTexture_ProcessingInfo *p, GDrawStats *stats)
|
|
{
|
|
GDrawHandle *t = (GDrawHandle *) p->p0;
|
|
D3D1X_(SUBRESOURCE_DATA) mipdata[24];
|
|
S32 i, w, h, nmips, bpp;
|
|
HRESULT hr = S_OK;
|
|
char *failed_call;
|
|
U8 *ptr;
|
|
|
|
// generate mip maps and set up descriptors for them
|
|
assert(p->i5 <= 24);
|
|
ptr = p->texture_data;
|
|
w = p->i0;
|
|
h = p->i1;
|
|
nmips = p->i5;
|
|
bpp = p->i6;
|
|
|
|
for (i=0; i < nmips; ++i) {
|
|
mipdata[i].pSysMem = ptr;
|
|
mipdata[i].SysMemPitch = RR_MAX(w >> i, 1) * bpp;
|
|
mipdata[i].SysMemSlicePitch = 0;
|
|
ptr += mipdata[i].SysMemPitch * RR_MAX(h >> i, 1);
|
|
|
|
// create mip data by downsampling
|
|
if (i)
|
|
gdraw_Downsample((U8 *) mipdata[i].pSysMem, mipdata[i].SysMemPitch, w >> i, h >> i,
|
|
(U8 *) mipdata[i-1].pSysMem, mipdata[i-1].SysMemPitch, bpp);
|
|
}
|
|
|
|
// actually create texture
|
|
D3D1X_(TEXTURE2D_DESC) desc = { w, h, nmips, 1, (DXGI_FORMAT) p->i3, { 1, 0 },
|
|
(p->i2 & GDRAW_MAKETEXTURE_FLAGS_updatable) ? D3D1X_(USAGE_DEFAULT) : D3D1X_(USAGE_IMMUTABLE),
|
|
D3D1X_(BIND_SHADER_RESOURCE), 0, 0 };
|
|
|
|
failed_call = "CreateTexture2D";
|
|
hr = gdraw->d3d_device->CreateTexture2D(&desc, mipdata, &t->handle.tex.d3d);
|
|
if (FAILED(hr)) goto done;
|
|
|
|
// and create a corresponding shader resource view
|
|
failed_call = "CreateShaderResourceView";
|
|
hr = gdraw->d3d_device->CreateShaderResourceView(t->handle.tex.d3d, NULL, &t->handle.tex.d3d_view);
|
|
|
|
done:
|
|
if (!FAILED(hr)) {
|
|
gdraw_HandleCacheAllocateEnd(t, p->i4, p->p1, (p->i2 & GDRAW_MAKETEXTURE_FLAGS_never_flush) ? GDRAW_HANDLE_STATE_pinned : GDRAW_HANDLE_STATE_locked);
|
|
stats->nonzero_flags |= GDRAW_STATS_alloc_tex;
|
|
stats->alloc_tex += 1;
|
|
stats->alloc_tex_bytes += p->i4;
|
|
} else {
|
|
safe_release(t->handle.tex.d3d);
|
|
safe_release(t->handle.tex.d3d_view);
|
|
|
|
gdraw_HandleCacheAllocateFail(t);
|
|
t = NULL;
|
|
report_d3d_error(hr, failed_call, " while creating texture");
|
|
}
|
|
|
|
IggyGDrawFree(p->texture_data);
|
|
return (GDrawTexture *) t;
|
|
}
|
|
|
|
static rrbool RADLINK gdraw_UpdateTextureBegin(GDrawTexture *t, void *unique_id, GDrawStats * /*stats*/)
|
|
{
|
|
return gdraw_HandleCacheLock((GDrawHandle *) t, unique_id);
|
|
}
|
|
|
|
static void RADLINK gdraw_UpdateTextureRect(GDrawTexture *t, void * /*unique_id*/, S32 x, S32 y, S32 stride, S32 w, S32 h, U8 *samples, gdraw_texture_format /*format*/)
|
|
{
|
|
GDrawHandle *s = (GDrawHandle *) t;
|
|
D3D1X_(BOX) box = { x, y, 0, x+w, y+h, 1 };
|
|
|
|
gdraw->d3d_context->UpdateSubresource(s->handle.tex.d3d, 0, &box, samples, stride, 0);
|
|
}
|
|
|
|
static void RADLINK gdraw_UpdateTextureEnd(GDrawTexture *t, void * /*unique_id*/, GDrawStats * /*stats*/)
|
|
{
|
|
gdraw_HandleCacheUnlock((GDrawHandle *) t);
|
|
}
|
|
|
|
static void RADLINK gdraw_FreeTexture(GDrawTexture *tt, void *unique_id, GDrawStats *stats)
|
|
{
|
|
GDrawHandle *t = (GDrawHandle *) tt;
|
|
assert(t != NULL); // @GDRAW_ASSERT
|
|
if (t->owner == unique_id || unique_id == NULL) {
|
|
if (t->cache == &gdraw->rendertargets) {
|
|
gdraw_HandleCacheUnlock(t);
|
|
// cache it by simply not freeing it
|
|
return;
|
|
}
|
|
|
|
gdraw_res_free(t, stats);
|
|
}
|
|
}
|
|
|
|
static rrbool RADLINK gdraw_TryToLockTexture(GDrawTexture *t, void *unique_id, GDrawStats * /*stats*/)
|
|
{
|
|
return gdraw_HandleCacheLock((GDrawHandle *) t, unique_id);
|
|
}
|
|
|
|
static void RADLINK gdraw_DescribeTexture(GDrawTexture *tex, GDraw_Texture_Description *desc)
|
|
{
|
|
GDrawHandle *p = (GDrawHandle *) tex;
|
|
desc->width = p->handle.tex.w;
|
|
desc->height = p->handle.tex.h;
|
|
desc->size_in_bytes = p->bytes;
|
|
}
|
|
|
|
static void RADLINK gdraw_SetAntialiasTexture(S32 width, U8 *rgba)
|
|
{
|
|
HRESULT hr;
|
|
|
|
safe_release(gdraw->aa_tex_view);
|
|
safe_release(gdraw->aa_tex);
|
|
|
|
D3D1X_(TEXTURE2D_DESC) desc = { width, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 }, D3D1X_(USAGE_IMMUTABLE), D3D1X_(BIND_SHADER_RESOURCE), 0, 0 };
|
|
D3D1X_(SUBRESOURCE_DATA) data = { rgba, width*4, 0 };
|
|
|
|
hr = gdraw->d3d_device->CreateTexture2D(&desc, &data, &gdraw->aa_tex);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateTexture2D", "");
|
|
return;
|
|
}
|
|
|
|
hr = gdraw->d3d_device->CreateShaderResourceView(gdraw->aa_tex, NULL, &gdraw->aa_tex_view);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateShaderResourceView", " while creating texture");
|
|
safe_release(gdraw->aa_tex);
|
|
return;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Vertex buffer creation/deletion
|
|
//
|
|
|
|
static rrbool RADLINK gdraw_MakeVertexBufferBegin(void *unique_id, gdraw_vformat /*vformat*/, S32 vbuf_size, S32 ibuf_size, GDraw_MakeVertexBuffer_ProcessingInfo *p, GDrawStats *stats)
|
|
{
|
|
// prepare staging buffers for the app to put data into
|
|
p->vertex_data = (U8 *) IggyGDrawMalloc(vbuf_size);
|
|
p->index_data = (U8 *) IggyGDrawMalloc(ibuf_size);
|
|
if (p->vertex_data && p->index_data) {
|
|
GDrawHandle *vb = gdraw_res_alloc_begin(gdraw->vbufcache, vbuf_size + ibuf_size, stats);
|
|
if (vb) {
|
|
vb->handle.vbuf.verts = NULL;
|
|
vb->handle.vbuf.inds = NULL;
|
|
|
|
p->vertex_data_length = vbuf_size;
|
|
p->index_data_length = ibuf_size;
|
|
p->p0 = vb;
|
|
p->p1 = unique_id;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (p->vertex_data)
|
|
IggyGDrawFree(p->vertex_data);
|
|
if (p->index_data)
|
|
IggyGDrawFree(p->index_data);
|
|
|
|
return false;
|
|
}
|
|
|
|
static rrbool RADLINK gdraw_MakeVertexBufferMore(GDraw_MakeVertexBuffer_ProcessingInfo * /*p*/)
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
static GDrawVertexBuffer * RADLINK gdraw_MakeVertexBufferEnd(GDraw_MakeVertexBuffer_ProcessingInfo *p, GDrawStats * /*stats*/)
|
|
{
|
|
GDrawHandle *vb = (GDrawHandle *) p->p0;
|
|
|
|
HRESULT hr;
|
|
D3D1X_(BUFFER_DESC) vbdesc = { p->vertex_data_length, D3D1X_(USAGE_IMMUTABLE), D3D1X_(BIND_VERTEX_BUFFER), 0, 0 };
|
|
D3D1X_(SUBRESOURCE_DATA) vbdata = { p->vertex_data, 0, 0 };
|
|
|
|
D3D1X_(BUFFER_DESC) ibdesc = { p->index_data_length, D3D1X_(USAGE_IMMUTABLE), D3D1X_(BIND_INDEX_BUFFER), 0, 0 };
|
|
D3D1X_(SUBRESOURCE_DATA) ibdata = { p->index_data, 0, 0 };
|
|
|
|
hr = gdraw->d3d_device->CreateBuffer(&vbdesc, &vbdata, &vb->handle.vbuf.verts);
|
|
if (!FAILED(hr))
|
|
hr = gdraw->d3d_device->CreateBuffer(&ibdesc, &ibdata, &vb->handle.vbuf.inds);
|
|
|
|
if (FAILED(hr)) {
|
|
safe_release(vb->handle.vbuf.verts);
|
|
safe_release(vb->handle.vbuf.inds);
|
|
|
|
gdraw_HandleCacheAllocateFail(vb);
|
|
vb = NULL;
|
|
|
|
report_d3d_error(hr, "CreateBuffer", " creating vertex buffer");
|
|
} else {
|
|
gdraw_HandleCacheAllocateEnd(vb, p->vertex_data_length + p->index_data_length, p->p1, GDRAW_HANDLE_STATE_locked);
|
|
}
|
|
|
|
IggyGDrawFree(p->vertex_data);
|
|
IggyGDrawFree(p->index_data);
|
|
|
|
return (GDrawVertexBuffer *) vb;
|
|
}
|
|
|
|
static rrbool RADLINK gdraw_TryLockVertexBuffer(GDrawVertexBuffer *vb, void *unique_id, GDrawStats * /*stats*/)
|
|
{
|
|
return gdraw_HandleCacheLock((GDrawHandle *) vb, unique_id);
|
|
}
|
|
|
|
static void RADLINK gdraw_FreeVertexBuffer(GDrawVertexBuffer *vb, void *unique_id, GDrawStats *stats)
|
|
{
|
|
GDrawHandle *h = (GDrawHandle *) vb;
|
|
assert(h != NULL); // @GDRAW_ASSERT
|
|
if (h->owner == unique_id)
|
|
gdraw_res_free(h, stats);
|
|
}
|
|
|
|
static void RADLINK gdraw_DescribeVertexBuffer(GDrawVertexBuffer *vbuf, GDraw_VertexBuffer_Description *desc)
|
|
{
|
|
GDrawHandle *p = (GDrawHandle *) vbuf;
|
|
desc->size_in_bytes = p->bytes;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create/free (or cache) render targets
|
|
//
|
|
|
|
static GDrawHandle *get_color_rendertarget(GDrawStats *stats)
|
|
{
|
|
char *failed_call;
|
|
|
|
// try to recycle LRU rendertarget
|
|
GDrawHandle *t = gdraw_HandleCacheGetLRU(&gdraw->rendertargets);
|
|
if (t) {
|
|
gdraw_HandleCacheLock(t, (void *) 1);
|
|
return t;
|
|
}
|
|
|
|
// ran out of RTs, allocate a new one
|
|
S32 size = gdraw->frametex_width * gdraw->frametex_height * 4;
|
|
if (gdraw->rendertargets.bytes_free < size) {
|
|
IggyGDrawSendWarning(NULL, "GDraw rendertarget allocation failed: hit size limit of %d bytes", gdraw->rendertargets.total_bytes);
|
|
return NULL;
|
|
}
|
|
|
|
t = gdraw_HandleCacheAllocateBegin(&gdraw->rendertargets);
|
|
if (!t) {
|
|
IggyGDrawSendWarning(NULL, "GDraw rendertarget allocation failed: hit handle limit");
|
|
return t;
|
|
}
|
|
|
|
D3D1X_(TEXTURE2D_DESC) desc = { gdraw->frametex_width, gdraw->frametex_height, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, { 1, 0 },
|
|
D3D1X_(USAGE_DEFAULT), D3D1X_(BIND_SHADER_RESOURCE) | D3D1X_(BIND_RENDER_TARGET), 0, 0 };
|
|
|
|
t->handle.tex.d3d = NULL;
|
|
t->handle.tex.d3d_view = NULL;
|
|
t->handle.tex.d3d_rtview = NULL;
|
|
|
|
HRESULT hr = gdraw->d3d_device->CreateTexture2D(&desc, NULL, &t->handle.tex.d3d);
|
|
failed_call = "CreateTexture2D";
|
|
if (!FAILED(hr)) {
|
|
hr = gdraw->d3d_device->CreateShaderResourceView(t->handle.tex.d3d, NULL, &t->handle.tex.d3d_view);
|
|
failed_call = "CreateTexture2D";
|
|
}
|
|
if (!FAILED(hr)) {
|
|
hr = gdraw->d3d_device->CreateRenderTargetView(t->handle.tex.d3d, NULL, &t->handle.tex.d3d_rtview);
|
|
failed_call = "CreateRenderTargetView";
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
safe_release(t->handle.tex.d3d);
|
|
safe_release(t->handle.tex.d3d_view);
|
|
safe_release(t->handle.tex.d3d_rtview);
|
|
gdraw_HandleCacheAllocateFail(t);
|
|
|
|
report_d3d_error(hr, failed_call, " creating rendertarget");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
gdraw_HandleCacheAllocateEnd(t, size, (void *) 1, GDRAW_HANDLE_STATE_locked);
|
|
stats->nonzero_flags |= GDRAW_STATS_alloc_tex;
|
|
stats->alloc_tex += 1;
|
|
stats->alloc_tex_bytes += size;
|
|
|
|
return t;
|
|
}
|
|
|
|
static ID3D1X(DepthStencilView) *get_rendertarget_depthbuffer(GDrawStats *stats)
|
|
{
|
|
if (!gdraw->depth_buffer[1]) {
|
|
char *failed_call;
|
|
assert(!gdraw->rt_depth_buffer);
|
|
|
|
D3D1X_(TEXTURE2D_DESC) desc = { gdraw->frametex_width, gdraw->frametex_height, 1, 1, DXGI_FORMAT_D24_UNORM_S8_UINT, { 1, 0 },
|
|
D3D1X_(USAGE_DEFAULT), D3D1X_(BIND_DEPTH_STENCIL), 0, 0 };
|
|
|
|
HRESULT hr = gdraw->d3d_device->CreateTexture2D(&desc, NULL, &gdraw->rt_depth_buffer);
|
|
failed_call = "CreateTexture2D";
|
|
if (!FAILED(hr)) {
|
|
hr = gdraw->d3d_device->CreateDepthStencilView(gdraw->rt_depth_buffer, NULL, &gdraw->depth_buffer[1]);
|
|
failed_call = "CreateDepthStencilView while creating rendertarget";
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, failed_call, "");
|
|
safe_release(gdraw->rt_depth_buffer);
|
|
safe_release(gdraw->depth_buffer[1]);
|
|
} else {
|
|
stats->nonzero_flags |= GDRAW_STATS_alloc_tex;
|
|
stats->alloc_tex += 1;
|
|
stats->alloc_tex_bytes += gdraw->frametex_width * gdraw->frametex_height * 4;
|
|
|
|
gdraw->d3d_context->ClearDepthStencilView(gdraw->depth_buffer[1], D3D1X_(CLEAR_DEPTH) | D3D1X_(CLEAR_STENCIL), 1.0f, 0);
|
|
}
|
|
}
|
|
|
|
return gdraw->depth_buffer[1];
|
|
}
|
|
|
|
static void flush_rendertargets(GDrawStats *stats)
|
|
{
|
|
gdraw_res_flush(&gdraw->rendertargets, stats);
|
|
|
|
safe_release(gdraw->depth_buffer[1]);
|
|
safe_release(gdraw->rt_depth_buffer);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Constant buffer layouts
|
|
//
|
|
|
|
struct VertexVars
|
|
{
|
|
F32 world[2][4];
|
|
F32 x_off[4];
|
|
F32 texgen_s[4];
|
|
F32 texgen_t[4];
|
|
F32 x3d[4];
|
|
F32 y3d[4];
|
|
F32 w3d[4];
|
|
};
|
|
|
|
struct PixelCommonVars
|
|
{
|
|
F32 color_mul[4];
|
|
F32 color_add[4];
|
|
F32 focal[4];
|
|
F32 rescale1[4];
|
|
};
|
|
|
|
struct PixelParaFilter
|
|
{
|
|
F32 clamp0[4], clamp1[4];
|
|
F32 color[4], color2[4];
|
|
F32 tc_off[4];
|
|
};
|
|
|
|
struct PixelParaBlur
|
|
{
|
|
F32 clamp[4];
|
|
F32 tap[9][4];
|
|
};
|
|
|
|
struct PixelParaColorMatrix
|
|
{
|
|
F32 data[5][4];
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Rendering helpers
|
|
//
|
|
|
|
static void disable_scissor(int force)
|
|
{
|
|
if (force || gdraw->scissor_state) {
|
|
// disable scissor by setting whole viewport as scissor rect
|
|
S32 x = gdraw->cview.x;
|
|
S32 y = gdraw->cview.y;
|
|
D3D1X_(RECT) r = { x, y, x + gdraw->cview.w, y + gdraw->cview.h };
|
|
|
|
gdraw->d3d_context->RSSetScissorRects(1, &r);
|
|
gdraw->scissor_state = 0;
|
|
}
|
|
}
|
|
|
|
static void set_viewport_raw(S32 x, S32 y, S32 w, S32 h)
|
|
{
|
|
D3D1X_(VIEWPORT) vp = { (ViewCoord) x, (ViewCoord) y, (ViewCoord) w, (ViewCoord) h, 0.0f, 1.0f };
|
|
gdraw->d3d_context->RSSetViewports(1, &vp);
|
|
gdraw->cview.x = x;
|
|
gdraw->cview.y = y;
|
|
gdraw->cview.w = w;
|
|
gdraw->cview.h = h;
|
|
|
|
disable_scissor(1);
|
|
}
|
|
|
|
static void set_projection_base(void)
|
|
{
|
|
// x3d = < viewproj.x, 0, 0, 0 >
|
|
// y3d = < 0, viewproj.y, 0, 0 >
|
|
// w3d = < viewproj.z, viewproj.w, 1.0, 1.0 >
|
|
|
|
memset(gdraw->projmat[0], 0, sizeof(gdraw->projmat));
|
|
gdraw->projmat[0][0] = gdraw->projection[0];
|
|
gdraw->projmat[1][1] = gdraw->projection[1];
|
|
gdraw->projmat[2][0] = gdraw->projection[2];
|
|
gdraw->projmat[2][1] = gdraw->projection[3];
|
|
|
|
gdraw->projmat[2][2] = 1.0;
|
|
gdraw->projmat[2][3] = 1.0;
|
|
}
|
|
|
|
static void set_projection_raw(S32 x0, S32 x1, S32 y0, S32 y1)
|
|
{
|
|
gdraw->projection[0] = 2.0f / (x1-x0);
|
|
gdraw->projection[1] = 2.0f / (y1-y0);
|
|
gdraw->projection[2] = (x1+x0)/(F32)(x0-x1);
|
|
gdraw->projection[3] = (y1+y0)/(F32)(y0-y1);
|
|
|
|
set_projection_base();
|
|
}
|
|
|
|
|
|
static void set_viewport(void)
|
|
{
|
|
if (gdraw->in_blur) {
|
|
set_viewport_raw(0, 0, gdraw->tpw, gdraw->tph);
|
|
return;
|
|
}
|
|
|
|
if (gdraw->cur == gdraw->frame) // if the rendering stack is empty
|
|
// render a tile-sized region to the user-request tile location
|
|
set_viewport_raw(gdraw->vx, gdraw->vy, gdraw->tw, gdraw->th);
|
|
else if (gdraw->cur->cached)
|
|
set_viewport_raw(0, 0, gdraw->cur->width, gdraw->cur->height);
|
|
else
|
|
// if on the render stack, draw a padded-tile-sized region at the origin
|
|
set_viewport_raw(0, 0, gdraw->tpw, gdraw->tph);
|
|
}
|
|
|
|
static void set_projection(void)
|
|
{
|
|
if (gdraw->in_blur) return;
|
|
if (gdraw->cur == gdraw->frame) // if the render stack is empty
|
|
set_projection_raw(gdraw->tx0, gdraw->tx0+gdraw->tw, gdraw->ty0+gdraw->th, gdraw->ty0);
|
|
else if (gdraw->cur->cached)
|
|
set_projection_raw(gdraw->cur->base_x, gdraw->cur->base_x+gdraw->cur->width, gdraw->cur->base_y, gdraw->cur->base_y+gdraw->cur->height);
|
|
else
|
|
set_projection_raw(gdraw->tx0p, gdraw->tx0p+gdraw->tpw, gdraw->ty0p+gdraw->tph, gdraw->ty0p);
|
|
}
|
|
|
|
static void clear_renderstate(void)
|
|
{
|
|
gdraw->d3d_context->ClearState();
|
|
}
|
|
|
|
static void set_common_renderstate()
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
S32 i;
|
|
|
|
clear_renderstate();
|
|
|
|
// all the render states we never change while drawing
|
|
d3d->IASetPrimitiveTopology(D3D1X_(PRIMITIVE_TOPOLOGY_TRIANGLELIST));
|
|
|
|
d3d->PSSetShaderResources(7, 1, &gdraw->aa_tex_view);
|
|
d3d->PSSetSamplers(7, 1, &gdraw->sampler_state[0][GDRAW_WRAP_clamp]);
|
|
|
|
// set a well-defined default sampler for all PS textures we use
|
|
for (i=0; i < 3; ++i)
|
|
d3d->PSSetSamplers(i, 1, &gdraw->sampler_state[0][GDRAW_WRAP_clamp]);
|
|
|
|
// reset our state caching
|
|
gdraw->scissor_state = ~0u;
|
|
gdraw->blend_mode = -1;
|
|
}
|
|
|
|
static void manual_clear(gswf_recti *r, GDrawStats *stats);
|
|
static void set_render_target(GDrawStats *stats);
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Begin/end rendering of a tile and per-frame processing
|
|
//
|
|
|
|
void gdraw_D3D1X_(SetRendertargetSize)(S32 w, S32 h)
|
|
{
|
|
if (gdraw && (w != gdraw->frametex_width || h != gdraw->frametex_height)) {
|
|
GDrawStats stats = { 0 };
|
|
gdraw->frametex_width = w;
|
|
gdraw->frametex_height = h;
|
|
flush_rendertargets(&stats);
|
|
}
|
|
}
|
|
|
|
void gdraw_D3D1X_(SetTileOrigin)(ID3D1X(RenderTargetView) *main_rt, ID3D1X(DepthStencilView) *main_ds, ID3D1X(ShaderResourceView) *non_msaa_rt, S32 x, S32 y)
|
|
{
|
|
D3D1X_(RENDER_TARGET_VIEW_DESC) desc;
|
|
|
|
if (gdraw->frame_done) {
|
|
++gdraw->frame_counter;
|
|
gdraw->frame_done = false;
|
|
}
|
|
|
|
main_rt->GetDesc(&desc);
|
|
|
|
gdraw->main_framebuffer = main_rt;
|
|
gdraw->main_resolve_target = non_msaa_rt;
|
|
gdraw->main_msaa = (desc.ViewDimension == D3D1X_(RTV_DIMENSION_TEXTURE2DMS));
|
|
gdraw->depth_buffer[0] = main_ds;
|
|
|
|
gdraw->vx = x;
|
|
gdraw->vy = y;
|
|
}
|
|
|
|
static void RADLINK gdraw_SetViewSizeAndWorldScale(S32 w, S32 h, F32 scalex, F32 scaley)
|
|
{
|
|
memset(gdraw->frame, 0, sizeof(gdraw->frame));
|
|
gdraw->cur = gdraw->frame;
|
|
gdraw->fw = w;
|
|
gdraw->fh = h;
|
|
gdraw->tw = w;
|
|
gdraw->th = h;
|
|
gdraw->world_to_pixel[0] = scalex;
|
|
gdraw->world_to_pixel[1] = scaley;
|
|
set_viewport();
|
|
}
|
|
|
|
// must include anything necessary for texture creation/update
|
|
static void RADLINK gdraw_RenderingBegin(void)
|
|
{
|
|
}
|
|
static void RADLINK gdraw_RenderingEnd(void)
|
|
{
|
|
}
|
|
|
|
static void RADLINK gdraw_RenderTileBegin(S32 x0, S32 y0, S32 x1, S32 y1, S32 pad, GDrawStats *stats)
|
|
{
|
|
if (x0 == 0 && y0 == 0 && x1 == gdraw->fw && y1 == gdraw->fh)
|
|
pad = 0;
|
|
|
|
gdraw->tx0 = x0;
|
|
gdraw->ty0 = y0;
|
|
gdraw->tw = x1-x0;
|
|
gdraw->th = y1-y0;
|
|
|
|
// padded region
|
|
gdraw->tx0p = RR_MAX(x0 - pad, 0);
|
|
gdraw->ty0p = RR_MAX(y0 - pad, 0);
|
|
gdraw->tpw = RR_MIN(x1 + pad, gdraw->fw) - gdraw->tx0p;
|
|
gdraw->tph = RR_MIN(y1 + pad, gdraw->fh) - gdraw->ty0p;
|
|
|
|
// make sure our rendertargets are large enough to contain the tile
|
|
if (gdraw->tpw > gdraw->frametex_width || gdraw->tph > gdraw->frametex_height) {
|
|
gdraw->frametex_width = RR_MAX(gdraw->tpw, gdraw->frametex_width);
|
|
gdraw->frametex_height = RR_MAX(gdraw->tph, gdraw->frametex_height);
|
|
|
|
flush_rendertargets(stats);
|
|
}
|
|
assert(gdraw->tpw <= gdraw->frametex_width && gdraw->tph <= gdraw->frametex_height);
|
|
|
|
// set up rendertargets we'll use
|
|
set_common_renderstate();
|
|
gdraw->d3d_context->ClearDepthStencilView(gdraw->depth_buffer[0], D3D1X_(CLEAR_DEPTH) | D3D1X_(CLEAR_STENCIL), 1.0f, 0);
|
|
if (gdraw->depth_buffer[1])
|
|
gdraw->d3d_context->ClearDepthStencilView(gdraw->depth_buffer[1], D3D1X_(CLEAR_DEPTH) | D3D1X_(CLEAR_STENCIL), 1.0f, 0);
|
|
|
|
set_projection();
|
|
set_viewport();
|
|
set_render_target(stats);
|
|
}
|
|
|
|
static void RADLINK gdraw_RenderTileEnd(GDrawStats * /*stats*/)
|
|
{
|
|
}
|
|
|
|
void gdraw_D3D1X_(NoMoreGDrawThisFrame)(void)
|
|
{
|
|
clear_renderstate();
|
|
gdraw->frame_done = true;
|
|
|
|
gdraw->last_dyn_maxalloc = gdraw->dyn_maxalloc;
|
|
gdraw->dyn_maxalloc = 0;
|
|
|
|
// reset dynamic buffer alloc position so they get DISCARDed
|
|
// next time around.
|
|
gdraw->dyn_vb.alloc_pos = 0;
|
|
gdraw->dyn_ib.alloc_pos = 0;
|
|
|
|
GDrawFence now = { gdraw->frame_counter };
|
|
gdraw_HandleCacheTick(gdraw->texturecache, now);
|
|
gdraw_HandleCacheTick(gdraw->vbufcache, now);
|
|
}
|
|
|
|
#define MAX_DEPTH_VALUE (1 << 13)
|
|
|
|
static void RADLINK gdraw_GetInfo(GDrawInfo *d)
|
|
{
|
|
d->num_stencil_bits = 8;
|
|
d->max_id = MAX_DEPTH_VALUE-2;
|
|
// for floating point depth, just use mantissa, e.g. 16-20 bits
|
|
d->buffer_format = GDRAW_BFORMAT_vbib;
|
|
d->shared_depth_stencil = 1;
|
|
d->always_mipmap = 1;
|
|
#ifndef GDRAW_D3D11_LEVEL9
|
|
d->max_texture_size = 8192;
|
|
d->conditional_nonpow2 = 0;
|
|
#else
|
|
d->max_texture_size = 2048;
|
|
d->conditional_nonpow2 = 1;
|
|
#endif
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Enable/disable rendertargets in stack fashion
|
|
//
|
|
|
|
static ID3D1X(RenderTargetView) *get_active_render_target()
|
|
{
|
|
if (gdraw->cur->color_buffer) {
|
|
unbind_resources(); // to make sure this RT isn't accidentally set as a texture (avoid D3D warnings)
|
|
return gdraw->cur->color_buffer->handle.tex.d3d_rtview;
|
|
} else
|
|
return gdraw->main_framebuffer;
|
|
}
|
|
|
|
static void set_render_target(GDrawStats *stats)
|
|
{
|
|
ID3D1X(RenderTargetView) *target = get_active_render_target();
|
|
if (target == gdraw->main_framebuffer) {
|
|
gdraw->d3d_context->OMSetRenderTargets(1, &target, gdraw->depth_buffer[0]);
|
|
gdraw->d3d_context->RSSetState(gdraw->raster_state[gdraw->main_msaa]);
|
|
} else {
|
|
ID3D1X(DepthStencilView) *depth = NULL;
|
|
if (gdraw->cur->flags & (GDRAW_TEXTUREDRAWBUFFER_FLAGS_needs_id | GDRAW_TEXTUREDRAWBUFFER_FLAGS_needs_stencil))
|
|
depth = get_rendertarget_depthbuffer(stats);
|
|
|
|
gdraw->d3d_context->OMSetRenderTargets(1, &target, depth);
|
|
gdraw->d3d_context->RSSetState(gdraw->raster_state[0]);
|
|
}
|
|
|
|
stats->nonzero_flags |= GDRAW_STATS_rendtarg;
|
|
stats->rendertarget_changes += 1;
|
|
}
|
|
|
|
static rrbool RADLINK gdraw_TextureDrawBufferBegin(gswf_recti *region, gdraw_texture_format /*format*/, U32 flags, void *owner, GDrawStats *stats)
|
|
{
|
|
GDrawFramebufferState *n = gdraw->cur+1;
|
|
GDrawHandle *t = NULL;
|
|
if (gdraw->tw == 0 || gdraw->th == 0) {
|
|
IggyGDrawSendWarning(NULL, "GDraw warning: w=0,h=0 rendertarget");
|
|
return false;
|
|
}
|
|
|
|
if (n >= &gdraw->frame[MAX_RENDER_STACK_DEPTH]) {
|
|
assert(0);
|
|
IggyGDrawSendWarning(NULL, "GDraw rendertarget nesting exceeds MAX_RENDER_STACK_DEPTH");
|
|
return false;
|
|
}
|
|
|
|
if (owner) {
|
|
// nyi
|
|
} else {
|
|
t = get_color_rendertarget(stats);
|
|
if (!t)
|
|
return false;
|
|
}
|
|
|
|
n->flags = flags;
|
|
n->color_buffer = t;
|
|
assert(n->color_buffer != NULL); // @GDRAW_ASSERT
|
|
|
|
++gdraw->cur;
|
|
gdraw->cur->cached = owner != NULL;
|
|
if (owner) {
|
|
gdraw->cur->base_x = region->x0;
|
|
gdraw->cur->base_y = region->y0;
|
|
gdraw->cur->width = region->x1 - region->x0;
|
|
gdraw->cur->height = region->y1 - region->y0;
|
|
}
|
|
|
|
set_render_target(stats);
|
|
assert(gdraw->frametex_width >= gdraw->tw && gdraw->frametex_height >= gdraw->th); // @GDRAW_ASSERT
|
|
|
|
S32 k = (S32) (t - gdraw->rendertargets.handle);
|
|
|
|
if (region) {
|
|
gswf_recti r;
|
|
S32 ox, oy, pad = 2; // 2 pixels of border on all sides
|
|
// 1 pixel turns out to be not quite enough with the interpolator precision we get.
|
|
|
|
if (gdraw->in_blur)
|
|
ox = oy = 0;
|
|
else
|
|
ox = gdraw->tx0p, oy = gdraw->ty0p;
|
|
|
|
// clamp region to tile
|
|
S32 xt0 = RR_MAX(region->x0 - ox, 0);
|
|
S32 yt0 = RR_MAX(region->y0 - oy, 0);
|
|
S32 xt1 = RR_MIN(region->x1 - ox, gdraw->tpw);
|
|
S32 yt1 = RR_MIN(region->y1 - oy, gdraw->tph);
|
|
|
|
// but the padding needs to clamp to render target bounds
|
|
r.x0 = RR_MAX(xt0 - pad, 0);
|
|
r.y0 = RR_MAX(yt0 - pad, 0);
|
|
r.x1 = RR_MIN(xt1 + pad, gdraw->frametex_width);
|
|
r.y1 = RR_MIN(yt1 + pad, gdraw->frametex_height);
|
|
|
|
if (r.x1 <= r.x0 || r.y1 <= r.y0) { // region doesn't intersect with current tile
|
|
--gdraw->cur;
|
|
gdraw_FreeTexture((GDrawTexture *) t, 0, stats);
|
|
// note: don't send a warning since this will happen during regular tiled rendering
|
|
return false;
|
|
}
|
|
|
|
manual_clear(&r, stats);
|
|
|
|
gdraw->rt_valid[k].x0 = xt0;
|
|
gdraw->rt_valid[k].y0 = yt0;
|
|
gdraw->rt_valid[k].x1 = xt1;
|
|
gdraw->rt_valid[k].y1 = yt1;
|
|
} else {
|
|
gdraw->d3d_context->ClearRenderTargetView(gdraw->cur->color_buffer->handle.tex.d3d_rtview, four_zeros);
|
|
gdraw->rt_valid[k].x0 = 0;
|
|
gdraw->rt_valid[k].y0 = 0;
|
|
gdraw->rt_valid[k].x1 = gdraw->frametex_width;
|
|
gdraw->rt_valid[k].y1 = gdraw->frametex_height;
|
|
}
|
|
|
|
if (!gdraw->in_blur) {
|
|
set_viewport();
|
|
set_projection();
|
|
} else {
|
|
set_viewport_raw(0, 0, gdraw->tpw, gdraw->tph);
|
|
set_projection_raw(0, gdraw->tpw, gdraw->tph, 0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static GDrawTexture *RADLINK gdraw_TextureDrawBufferEnd(GDrawStats *stats)
|
|
{
|
|
GDrawFramebufferState *n = gdraw->cur;
|
|
GDrawFramebufferState *m = --gdraw->cur;
|
|
if (gdraw->tw == 0 || gdraw->th == 0) return 0;
|
|
|
|
if (n >= &gdraw->frame[MAX_RENDER_STACK_DEPTH])
|
|
return 0; // already returned a warning in Begin
|
|
|
|
assert(m >= gdraw->frame); // bug in Iggy -- unbalanced
|
|
|
|
if (m != gdraw->frame) {
|
|
assert(m->color_buffer != NULL); // @GDRAW_ASSERT
|
|
}
|
|
assert(n->color_buffer != NULL); // @GDRAW_ASSERT
|
|
|
|
// switch back to old render target
|
|
set_render_target(stats);
|
|
|
|
// if we're at the root, set the viewport back
|
|
set_viewport();
|
|
set_projection();
|
|
|
|
return (GDrawTexture *) n->color_buffer;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Clear stencil/depth buffers
|
|
//
|
|
// Open question whether we'd be better off finding bounding boxes
|
|
// and only clearing those; it depends exactly how fast clearing works.
|
|
//
|
|
|
|
static void RADLINK gdraw_ClearStencilBits(U32 /*bits*/)
|
|
{
|
|
gdraw->d3d_context->ClearDepthStencilView(gdraw->depth_buffer[0], D3D1X_(CLEAR_STENCIL), 1.0f, 0);
|
|
if (gdraw->depth_buffer[1])
|
|
gdraw->d3d_context->ClearDepthStencilView(gdraw->depth_buffer[1], D3D1X_(CLEAR_STENCIL), 1.0f, 0);
|
|
}
|
|
|
|
// this only happens rarely (hopefully never) if we use the depth buffer,
|
|
// so we can just clear the whole thing
|
|
static void RADLINK gdraw_ClearID(void)
|
|
{
|
|
gdraw->d3d_context->ClearDepthStencilView(gdraw->depth_buffer[0], D3D1X_(CLEAR_DEPTH), 1.0f, 0);
|
|
if (gdraw->depth_buffer[1])
|
|
gdraw->d3d_context->ClearDepthStencilView(gdraw->depth_buffer[1], D3D1X_(CLEAR_DEPTH), 1.0f, 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Set all the render state from GDrawRenderState
|
|
//
|
|
// This also is responsible for getting the framebuffer into a texture
|
|
// if the read-modify-write blend operation can't be expressed with
|
|
// the native blend operators. (E.g. "screen")
|
|
//
|
|
|
|
// convert an ID request to a value suitable for the depth buffer,
|
|
// assuming the depth buffer has been mappped to 0..1
|
|
static F32 depth_from_id(S32 id)
|
|
{
|
|
return 1.0f - ((F32) id + 1.0f) / MAX_DEPTH_VALUE;
|
|
}
|
|
|
|
static void set_texture(S32 texunit, GDrawTexture *tex, rrbool nearest, S32 wrap)
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
|
|
if (tex == NULL) {
|
|
ID3D1X(ShaderResourceView) *notex = NULL;
|
|
d3d->PSSetShaderResources(texunit, 1, ¬ex);
|
|
} else {
|
|
GDrawHandle *h = (GDrawHandle *) tex;
|
|
d3d->PSSetShaderResources(texunit, 1, &h->handle.tex.d3d_view);
|
|
d3d->PSSetSamplers(texunit, 1, &gdraw->sampler_state[nearest][wrap]);
|
|
}
|
|
}
|
|
|
|
static void RADLINK gdraw_Set3DTransform(F32 *mat)
|
|
{
|
|
if (mat == NULL)
|
|
gdraw->use_3d = 0;
|
|
else {
|
|
gdraw->use_3d = 1;
|
|
memcpy(gdraw->xform_3d, mat, sizeof(gdraw->xform_3d));
|
|
}
|
|
}
|
|
|
|
static int set_renderstate_full(S32 vertex_format, GDrawRenderState *r, GDrawStats * /* stats */, const F32 *rescale1)
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
|
|
// set vertex shader
|
|
set_vertex_shader(d3d, gdraw->vert[vertex_format].vshader);
|
|
|
|
// set vertex shader constants
|
|
if (VertexVars *vvars = (VertexVars *) map_buffer(gdraw->d3d_context, gdraw->cb_vertex, true)) {
|
|
F32 depth = depth_from_id(r->id);
|
|
if (!r->use_world_space)
|
|
gdraw_ObjectSpace(vvars->world[0], r->o2w, depth, 0.0f);
|
|
else
|
|
gdraw_WorldSpace(vvars->world[0], gdraw->world_to_pixel, depth, 0.0f);
|
|
|
|
memcpy(&vvars->x_off, r->edge_matrix, 4*sizeof(F32));
|
|
|
|
if (r->texgen0_enabled) {
|
|
memcpy(&vvars->texgen_s, r->s0_texgen, 4*sizeof(F32));
|
|
memcpy(&vvars->texgen_t, r->t0_texgen, 4*sizeof(F32));
|
|
}
|
|
|
|
if (gdraw->use_3d)
|
|
memcpy(vvars->x3d, gdraw->xform_3d, 12*sizeof(F32));
|
|
else
|
|
memcpy(vvars->x3d, gdraw->projmat, 12*sizeof(F32));
|
|
|
|
unmap_buffer(gdraw->d3d_context, gdraw->cb_vertex);
|
|
|
|
d3d->VSSetConstantBuffers(0, 1, &gdraw->cb_vertex);
|
|
}
|
|
|
|
// set the blend mode
|
|
int blend_mode = r->blend_mode;
|
|
if (blend_mode != gdraw->blend_mode) {
|
|
gdraw->blend_mode = blend_mode;
|
|
d3d->OMSetBlendState(gdraw->blend_state[blend_mode], four_zeros, ~0u);
|
|
}
|
|
|
|
// set the fragment program
|
|
if (blend_mode != GDRAW_BLEND_special) {
|
|
int which = r->tex0_mode;
|
|
assert(which >= 0 && which < sizeof(gdraw->fprog) / sizeof(*gdraw->fprog));
|
|
|
|
int additive = 0;
|
|
if (r->cxf_add) {
|
|
additive = 1;
|
|
if (r->cxf_add[3]) additive = 2;
|
|
}
|
|
|
|
ID3D1X(PixelShader) *program = gdraw->fprog[which][additive].pshader;
|
|
if (r->stencil_set) {
|
|
// in stencil set mode, prefer not doing any shading at all
|
|
// but if alpha test is on, we need to make an exception
|
|
|
|
#ifndef GDRAW_D3D11_LEVEL9 // level9 can't do NULL PS it seems
|
|
if (which != GDRAW_TEXTURE_alpha_test)
|
|
program = NULL;
|
|
else
|
|
#endif
|
|
{
|
|
gdraw->blend_mode = -1;
|
|
d3d->OMSetBlendState(gdraw->blend_no_color_write, four_zeros, ~0u);
|
|
}
|
|
}
|
|
|
|
set_pixel_shader(d3d, program);
|
|
} else
|
|
set_pixel_shader(d3d, gdraw->exceptional_blend[r->special_blend].pshader);
|
|
|
|
set_texture(0, r->tex[0], r->nearest0, r->wrap0);
|
|
|
|
// pixel shader constants
|
|
if (PixelCommonVars *pvars = (PixelCommonVars *) map_buffer(gdraw->d3d_context, gdraw->cb_ps_common, true)) {
|
|
memcpy(pvars->color_mul, r->color, 4*sizeof(float));
|
|
|
|
if (r->cxf_add) {
|
|
pvars->color_add[0] = r->cxf_add[0] / 255.0f;
|
|
pvars->color_add[1] = r->cxf_add[1] / 255.0f;
|
|
pvars->color_add[2] = r->cxf_add[2] / 255.0f;
|
|
pvars->color_add[3] = r->cxf_add[3] / 255.0f;
|
|
} else
|
|
pvars->color_add[0] = pvars->color_add[1] = pvars->color_add[2] = pvars->color_add[3] = 0.0f;
|
|
|
|
if (r->tex0_mode == GDRAW_TEXTURE_focal_gradient) memcpy(pvars->focal, r->focal_point, 4*sizeof(float));
|
|
if (r->blend_mode == GDRAW_BLEND_special) memcpy(pvars->rescale1, rescale1, 4*sizeof(float));
|
|
unmap_buffer(gdraw->d3d_context, gdraw->cb_ps_common);
|
|
d3d->PSSetConstantBuffers(0, 1, &gdraw->cb_ps_common);
|
|
}
|
|
|
|
// Set pixel operation states
|
|
if (r->scissor) {
|
|
D3D1X_(RECT) s;
|
|
gdraw->scissor_state = 1;
|
|
if (gdraw->cur == gdraw->frame) {
|
|
s.left = r->scissor_rect.x0 + gdraw->vx - gdraw->tx0;
|
|
s.top = r->scissor_rect.y0 + gdraw->vy - gdraw->ty0;
|
|
s.right = r->scissor_rect.x1 + gdraw->vx - gdraw->tx0;
|
|
s.bottom = r->scissor_rect.y1 + gdraw->vy - gdraw->ty0;
|
|
} else {
|
|
s.left = r->scissor_rect.x0 - gdraw->tx0p;
|
|
s.top = r->scissor_rect.y0 - gdraw->ty0p;
|
|
s.right = r->scissor_rect.x1 - gdraw->tx0p;
|
|
s.bottom = r->scissor_rect.y1 - gdraw->ty0p;
|
|
}
|
|
d3d->RSSetScissorRects(1, &s);
|
|
} else if (r->scissor != gdraw->scissor_state)
|
|
disable_scissor(0);
|
|
|
|
if (r->stencil_set | r->stencil_test)
|
|
d3d->OMSetDepthStencilState(stencil_state_cache_lookup(r->set_id, r->test_id, r->stencil_test, r->stencil_set), 255);
|
|
else
|
|
d3d->OMSetDepthStencilState(gdraw->depth_state[r->set_id][r->test_id], 0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static RADINLINE int set_renderstate(S32 vertex_format, GDrawRenderState *r, GDrawStats *stats)
|
|
{
|
|
static const F32 unit_rescale[4] = { 1.0f, 1.0f, 0.0f, 0.0f };
|
|
if (r->identical_state) {
|
|
// fast path: only need to change vertex shader, other state is the same
|
|
set_vertex_shader(gdraw->d3d_context, gdraw->vert[vertex_format].vshader);
|
|
return 1;
|
|
} else
|
|
return set_renderstate_full(vertex_format, r, stats, unit_rescale);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Vertex formats
|
|
//
|
|
|
|
static D3D1X_(INPUT_ELEMENT_DESC) vformat_v2[] = {
|
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D1X_(INPUT_PER_VERTEX_DATA), 0 },
|
|
};
|
|
|
|
static D3D1X_(INPUT_ELEMENT_DESC) vformat_v2aa[] = {
|
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D1X_(INPUT_PER_VERTEX_DATA), 0 },
|
|
{ "TEXCOORD", 0, DXGI_FORMAT_R16G16B16A16_SINT, 0, 8, D3D1X_(INPUT_PER_VERTEX_DATA), 0 },
|
|
};
|
|
|
|
static D3D1X_(INPUT_ELEMENT_DESC) vformat_v2tc2[] = {
|
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D1X_(INPUT_PER_VERTEX_DATA), 0 },
|
|
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D1X_(INPUT_PER_VERTEX_DATA), 0 },
|
|
};
|
|
|
|
static struct gdraw_vertex_format_desc {
|
|
D3D1X_(INPUT_ELEMENT_DESC) *desc;
|
|
U32 nelem;
|
|
} vformats[ASSERT_COUNT(GDRAW_vformat__basic_count, 3)] = {
|
|
vformat_v2, 1, // GDRAW_vformat_v2
|
|
vformat_v2aa, 2, // GDRAW_vformat_v2aa
|
|
vformat_v2tc2, 2, // GDRAW_vforamt_v2tc2
|
|
};
|
|
|
|
static int vertsize[GDRAW_vformat__basic_count] = {
|
|
8, // GDRAW_vformat_v2
|
|
16, // GDRAW_vformat_v2aa
|
|
16, // GDRAW_vformat_v2tc2
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Draw triangles with a given renderstate
|
|
//
|
|
|
|
static void tag_resources(void *r1, void *r2=NULL, void *r3=NULL, void *r4=NULL)
|
|
{
|
|
U64 now = gdraw->frame_counter;
|
|
if (r1) ((GDrawHandle *) r1)->fence.value = now;
|
|
if (r2) ((GDrawHandle *) r2)->fence.value = now;
|
|
if (r3) ((GDrawHandle *) r3)->fence.value = now;
|
|
if (r4) ((GDrawHandle *) r4)->fence.value = now;
|
|
}
|
|
|
|
static void RADLINK gdraw_DrawIndexedTriangles(GDrawRenderState *r, GDrawPrimitive *p, GDrawVertexBuffer *buf, GDrawStats *stats)
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
GDrawHandle *vb = (GDrawHandle *) buf;
|
|
int vfmt = p->vertex_format;
|
|
assert(vfmt >= 0 && vfmt < GDRAW_vformat__count);
|
|
|
|
if (!set_renderstate(vfmt, r, stats))
|
|
return;
|
|
|
|
UINT stride = vertsize[vfmt];
|
|
d3d->IASetInputLayout(gdraw->inlayout[vfmt]);
|
|
|
|
if (vb) {
|
|
UINT offs = (UINT) (UINTa) p->vertices;
|
|
|
|
d3d->IASetVertexBuffers(0, 1, &vb->handle.vbuf.verts, &stride, &offs);
|
|
d3d->IASetIndexBuffer(vb->handle.vbuf.inds, DXGI_FORMAT_R16_UINT, (UINT) (UINTa) p->indices);
|
|
d3d->DrawIndexed(p->num_indices, 0, 0);
|
|
} else if (p->indices) {
|
|
U32 vbytes = p->num_vertices * stride;
|
|
U32 ibytes = p->num_indices * 2;
|
|
|
|
if (void *vbptr = start_write_dyn(&gdraw->dyn_vb, vbytes)) {
|
|
memcpy(vbptr, p->vertices, vbytes);
|
|
UINT vboffs = end_write_dyn(&gdraw->dyn_vb);
|
|
|
|
if (void *ibptr = start_write_dyn(&gdraw->dyn_ib, ibytes)) {
|
|
memcpy(ibptr, p->indices, ibytes);
|
|
UINT iboffs = end_write_dyn(&gdraw->dyn_ib);
|
|
|
|
d3d->IASetVertexBuffers(0, 1, &gdraw->dyn_vb.buffer, &stride, &vboffs);
|
|
d3d->IASetIndexBuffer(gdraw->dyn_ib.buffer, DXGI_FORMAT_R16_UINT, iboffs);
|
|
d3d->DrawIndexed(p->num_indices, 0, 0);
|
|
}
|
|
}
|
|
} else { // dynamic quads
|
|
assert(p->num_vertices % 4 == 0);
|
|
d3d->IASetIndexBuffer(gdraw->quad_ib, DXGI_FORMAT_R16_UINT, 0);
|
|
|
|
if (gdraw->max_quad_vert_count) {
|
|
S32 pos = 0;
|
|
while (pos < p->num_vertices) {
|
|
S32 vert_count = RR_MIN(p->num_vertices - pos, gdraw->max_quad_vert_count);
|
|
UINT chunk_bytes = vert_count * stride;
|
|
|
|
if (void *vbptr = start_write_dyn(&gdraw->dyn_vb, chunk_bytes)) {
|
|
memcpy(vbptr, (U8 *)p->vertices + pos*stride, chunk_bytes);
|
|
UINT offs = end_write_dyn(&gdraw->dyn_vb);
|
|
|
|
d3d->IASetVertexBuffers(0, 1, &gdraw->dyn_vb.buffer, &stride, &offs);
|
|
d3d->DrawIndexed((vert_count >> 2) * 6, 0, 0);
|
|
}
|
|
pos += vert_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
tag_resources(vb, r->tex[0], r->tex[1]);
|
|
|
|
stats->nonzero_flags |= GDRAW_STATS_batches;
|
|
stats->num_batches += 1;
|
|
stats->drawn_indices += p->num_indices;
|
|
stats->drawn_vertices += p->num_vertices;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Flash 8 filter effects
|
|
//
|
|
|
|
static void *start_ps_constants(ID3D1X(Buffer) *buffer)
|
|
{
|
|
return map_buffer(gdraw->d3d_context, buffer, true);
|
|
}
|
|
|
|
static void end_ps_constants(ID3D1X(Buffer) *buffer)
|
|
{
|
|
unmap_buffer(gdraw->d3d_context, buffer);
|
|
gdraw->d3d_context->PSSetConstantBuffers(1, 1, &buffer);
|
|
}
|
|
|
|
static void set_pixel_constant(F32 *constant, F32 x, F32 y, F32 z, F32 w)
|
|
{
|
|
constant[0] = x;
|
|
constant[1] = y;
|
|
constant[2] = z;
|
|
constant[3] = w;
|
|
}
|
|
|
|
// caller sets up texture coordinates
|
|
static void do_screen_quad(gswf_recti *s, const F32 *tc, GDrawStats *stats)
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
F32 px0 = (F32) s->x0, py0 = (F32) s->y0, px1 = (F32) s->x1, py1 = (F32) s->y1;
|
|
|
|
// generate vertex data
|
|
gswf_vertex_xyst *vert = (gswf_vertex_xyst *) start_write_dyn(&gdraw->dyn_vb, 4 * sizeof(gswf_vertex_xyst));
|
|
if (!vert)
|
|
return;
|
|
|
|
vert[0].x = px0; vert[0].y = py0; vert[0].s = tc[0]; vert[0].t = tc[1];
|
|
vert[1].x = px1; vert[1].y = py0; vert[1].s = tc[2]; vert[1].t = tc[1];
|
|
vert[2].x = px0; vert[2].y = py1; vert[2].s = tc[0]; vert[2].t = tc[3];
|
|
vert[3].x = px1; vert[3].y = py1; vert[3].s = tc[2]; vert[3].t = tc[3];
|
|
UINT offs = end_write_dyn(&gdraw->dyn_vb);
|
|
UINT stride = sizeof(gswf_vertex_xyst);
|
|
|
|
if (VertexVars *vvars = (VertexVars *) map_buffer(gdraw->d3d_context, gdraw->cb_vertex, true)) {
|
|
gdraw_PixelSpace(vvars->world[0]);
|
|
memcpy(vvars->x3d, gdraw->projmat, 12*sizeof(F32));
|
|
unmap_buffer(gdraw->d3d_context, gdraw->cb_vertex);
|
|
d3d->VSSetConstantBuffers(0, 1, &gdraw->cb_vertex);
|
|
|
|
set_vertex_shader(d3d, gdraw->vert[GDRAW_vformat_v2tc2].vshader);
|
|
|
|
d3d->IASetInputLayout(gdraw->inlayout[GDRAW_vformat_v2tc2]);
|
|
d3d->IASetVertexBuffers(0, 1, &gdraw->dyn_vb.buffer, &stride, &offs);
|
|
d3d->IASetPrimitiveTopology(D3D1X_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP));
|
|
d3d->Draw(4, 0);
|
|
d3d->IASetPrimitiveTopology(D3D1X_(PRIMITIVE_TOPOLOGY_TRIANGLELIST));
|
|
|
|
stats->nonzero_flags |= GDRAW_STATS_batches;
|
|
stats->num_batches += 1;
|
|
stats->drawn_indices += 6;
|
|
stats->drawn_vertices += 4;
|
|
}
|
|
}
|
|
|
|
static void manual_clear(gswf_recti *r, GDrawStats *stats)
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
|
|
// go to known render state
|
|
d3d->OMSetBlendState(gdraw->blend_state[GDRAW_BLEND_none], four_zeros, ~0u);
|
|
d3d->OMSetDepthStencilState(gdraw->depth_state[0][0], 0);
|
|
gdraw->blend_mode = GDRAW_BLEND_none;
|
|
|
|
set_viewport_raw(0, 0, gdraw->frametex_width, gdraw->frametex_height);
|
|
set_projection_raw(0, gdraw->frametex_width, gdraw->frametex_height, 0);
|
|
set_pixel_shader(d3d, gdraw->clear_ps.pshader);
|
|
|
|
if (PixelCommonVars *pvars = (PixelCommonVars *) map_buffer(gdraw->d3d_context, gdraw->cb_ps_common, true)) {
|
|
memset(pvars, 0, sizeof(*pvars));
|
|
unmap_buffer(gdraw->d3d_context, gdraw->cb_ps_common);
|
|
d3d->PSSetConstantBuffers(0, 1, &gdraw->cb_ps_common);
|
|
|
|
do_screen_quad(r, four_zeros, stats);
|
|
}
|
|
}
|
|
|
|
static void gdraw_DriverBlurPass(GDrawRenderState *r, int taps, float *data, gswf_recti *s, float *tc, float /*height_max*/, float *clamp, GDrawStats *gstats)
|
|
{
|
|
set_texture(0, r->tex[0], false, GDRAW_WRAP_clamp);
|
|
|
|
set_pixel_shader(gdraw->d3d_context, gdraw->blur_prog[taps].pshader);
|
|
PixelParaBlur *para = (PixelParaBlur *) start_ps_constants(gdraw->cb_blur);
|
|
memcpy(para->clamp, clamp, 4 * sizeof(float));
|
|
memcpy(para->tap, data, taps * 4 * sizeof(float));
|
|
end_ps_constants(gdraw->cb_blur);
|
|
|
|
do_screen_quad(s, tc, gstats);
|
|
tag_resources(r->tex[0]);
|
|
}
|
|
|
|
static void gdraw_Colormatrix(GDrawRenderState *r, gswf_recti *s, float *tc, GDrawStats *stats)
|
|
{
|
|
if (!gdraw_TextureDrawBufferBegin(s, GDRAW_TEXTURE_FORMAT_rgba32, GDRAW_TEXTUREDRAWBUFFER_FLAGS_needs_color | GDRAW_TEXTUREDRAWBUFFER_FLAGS_needs_alpha, 0, stats))
|
|
return;
|
|
|
|
set_texture(0, r->tex[0], false, GDRAW_WRAP_clamp);
|
|
set_pixel_shader(gdraw->d3d_context, gdraw->colormatrix.pshader);
|
|
|
|
PixelParaColorMatrix *para = (PixelParaColorMatrix *) start_ps_constants(gdraw->cb_colormatrix);
|
|
memcpy(para->data, r->shader_data, 5 * 4 * sizeof(float));
|
|
end_ps_constants(gdraw->cb_colormatrix);
|
|
|
|
do_screen_quad(s, tc, stats);
|
|
tag_resources(r->tex[0]);
|
|
r->tex[0] = gdraw_TextureDrawBufferEnd(stats);
|
|
}
|
|
|
|
static gswf_recti *get_valid_rect(GDrawTexture *tex)
|
|
{
|
|
GDrawHandle *h = (GDrawHandle *) tex;
|
|
S32 n = (S32) (h - gdraw->rendertargets.handle);
|
|
assert(n >= 0 && n <= MAX_RENDER_STACK_DEPTH+1);
|
|
return &gdraw->rt_valid[n];
|
|
}
|
|
|
|
static void set_clamp_constant(F32 *constant, GDrawTexture *tex)
|
|
{
|
|
gswf_recti *s = get_valid_rect(tex);
|
|
// when we make the valid data, we make sure there is an extra empty pixel at the border
|
|
set_pixel_constant(constant,
|
|
(s->x0-0.5f) / gdraw->frametex_width,
|
|
(s->y0-0.5f) / gdraw->frametex_height,
|
|
(s->x1+0.5f) / gdraw->frametex_width,
|
|
(s->y1+0.5f) / gdraw->frametex_height);
|
|
}
|
|
|
|
static void gdraw_Filter(GDrawRenderState *r, gswf_recti *s, float *tc, int isbevel, GDrawStats *stats)
|
|
{
|
|
if (!gdraw_TextureDrawBufferBegin(s, GDRAW_TEXTURE_FORMAT_rgba32, GDRAW_TEXTUREDRAWBUFFER_FLAGS_needs_color | GDRAW_TEXTUREDRAWBUFFER_FLAGS_needs_alpha, NULL, stats))
|
|
return;
|
|
|
|
set_texture(0, r->tex[0], false, GDRAW_WRAP_clamp);
|
|
set_texture(1, r->tex[1], false, GDRAW_WRAP_clamp);
|
|
set_texture(2, r->tex[2], false, GDRAW_WRAP_clamp);
|
|
set_pixel_shader(gdraw->d3d_context, gdraw->filter_prog[isbevel][r->filter_mode].pshader);
|
|
|
|
PixelParaFilter *para = (PixelParaFilter *) start_ps_constants(gdraw->cb_filter);
|
|
set_clamp_constant(para->clamp0, r->tex[0]);
|
|
set_clamp_constant(para->clamp1, r->tex[1]);
|
|
set_pixel_constant(para->color, r->shader_data[0], r->shader_data[1], r->shader_data[2], r->shader_data[3]);
|
|
set_pixel_constant(para->color2, r->shader_data[8], r->shader_data[9], r->shader_data[10], r->shader_data[11]);
|
|
set_pixel_constant(para->tc_off, -r->shader_data[4] / (F32)gdraw->frametex_width, -r->shader_data[5] / (F32)gdraw->frametex_height, r->shader_data[6], 0);
|
|
end_ps_constants(gdraw->cb_filter);
|
|
|
|
do_screen_quad(s, tc, stats);
|
|
tag_resources(r->tex[0], r->tex[1], r->tex[2]);
|
|
r->tex[0] = gdraw_TextureDrawBufferEnd(stats);
|
|
}
|
|
|
|
static void RADLINK gdraw_FilterQuad(GDrawRenderState *r, S32 x0, S32 y0, S32 x1, S32 y1, GDrawStats *stats)
|
|
{
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
F32 tc[4];
|
|
gswf_recti s;
|
|
|
|
// clip to tile boundaries
|
|
s.x0 = RR_MAX(x0, gdraw->tx0p);
|
|
s.y0 = RR_MAX(y0, gdraw->ty0p);
|
|
s.x1 = RR_MIN(x1, gdraw->tx0p + gdraw->tpw);
|
|
s.y1 = RR_MIN(y1, gdraw->ty0p + gdraw->tph);
|
|
if (s.x1 < s.x0 || s.y1 < s.y0)
|
|
return;
|
|
|
|
tc[0] = (s.x0 - gdraw->tx0p) / (F32) gdraw->frametex_width;
|
|
tc[1] = (s.y0 - gdraw->ty0p) / (F32) gdraw->frametex_height;
|
|
tc[2] = (s.x1 - gdraw->tx0p) / (F32) gdraw->frametex_width;
|
|
tc[3] = (s.y1 - gdraw->ty0p) / (F32) gdraw->frametex_height;
|
|
|
|
// clear to known render state
|
|
d3d->OMSetBlendState(gdraw->blend_state[GDRAW_BLEND_none], four_zeros, ~0u);
|
|
d3d->OMSetDepthStencilState(gdraw->depth_state[0][0], 0);
|
|
disable_scissor(0);
|
|
gdraw->blend_mode = GDRAW_BLEND_none;
|
|
|
|
if (r->blend_mode == GDRAW_BLEND_filter) {
|
|
switch (r->filter) {
|
|
case GDRAW_FILTER_blur: {
|
|
GDrawBlurInfo b;
|
|
gswf_recti bounds = *get_valid_rect(r->tex[0]);
|
|
gdraw_ShiftRect(&s, &s, -gdraw->tx0p, -gdraw->ty0p); // blur uses physical rendertarget coordinates
|
|
|
|
b.BlurPass = gdraw_DriverBlurPass;
|
|
b.w = gdraw->tpw;
|
|
b.h = gdraw->tph;
|
|
b.frametex_width = gdraw->frametex_width;
|
|
b.frametex_height = gdraw->frametex_height;
|
|
|
|
// blur needs to draw with multiple passes, so set up special state
|
|
gdraw->in_blur = true;
|
|
|
|
// do the blur
|
|
gdraw_Blur(&gdraw_funcs, &b, r, &s, &bounds, stats);
|
|
|
|
// restore the normal state
|
|
gdraw->in_blur = false;
|
|
set_viewport();
|
|
set_projection();
|
|
break;
|
|
}
|
|
|
|
case GDRAW_FILTER_colormatrix:
|
|
gdraw_Colormatrix(r, &s, tc, stats);
|
|
break;
|
|
|
|
case GDRAW_FILTER_dropshadow:
|
|
gdraw_Filter(r, &s, tc, 0, stats);
|
|
break;
|
|
|
|
case GDRAW_FILTER_bevel:
|
|
gdraw_Filter(r, &s, tc, 1, stats);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
} else {
|
|
GDrawHandle *blend_tex = NULL;
|
|
|
|
// for crazy blend modes, we need to read back from the framebuffer
|
|
// and do the blending in the pixel shader. we do this with copies
|
|
// rather than trying to render-to-texture-all-along, because we want
|
|
// to be able to render over the user's existing framebuffer, which might
|
|
// not be a texture. note that this isn't optimal when MSAA is on!
|
|
F32 rescale1[4] = { 1.0f, 1.0f, 0.0f, 0.0f };
|
|
if (r->blend_mode == GDRAW_BLEND_special) {
|
|
ID3D1XContext *d3d = gdraw->d3d_context;
|
|
ID3D1X(Resource) *cur_rt_rsrc;
|
|
get_active_render_target()->GetResource(&cur_rt_rsrc);
|
|
|
|
if (gdraw->cur == gdraw->frame && gdraw->main_msaa) {
|
|
// source surface is main framebuffer and it uses MSAA. just resolve it first.
|
|
D3D1X_(SHADER_RESOURCE_VIEW_DESC) desc;
|
|
D3D1X_(TEXTURE2D_DESC) texdesc;
|
|
ID3D1X(Texture2D) *resolve_tex;
|
|
|
|
gdraw->main_resolve_target->GetDesc(&desc);
|
|
gdraw->main_resolve_target->GetResource((ID3D1X(Resource) **) &resolve_tex);
|
|
resolve_tex->GetDesc(&texdesc);
|
|
d3d->ResolveSubresource(resolve_tex, 0, cur_rt_rsrc, 0, desc.Format);
|
|
resolve_tex->Release();
|
|
|
|
stats->nonzero_flags |= GDRAW_STATS_blits;
|
|
stats->num_blits += 1;
|
|
stats->num_blit_pixels += texdesc.Width * texdesc.Height;
|
|
|
|
d3d->PSSetShaderResources(1, 1, &gdraw->main_resolve_target);
|
|
d3d->PSSetSamplers(1, 1, &gdraw->sampler_state[0][GDRAW_WRAP_clamp]);
|
|
|
|
// calculate texture coordinate remapping
|
|
rescale1[0] = gdraw->frametex_width / (F32) texdesc.Width;
|
|
rescale1[1] = gdraw->frametex_height / (F32) texdesc.Height;
|
|
rescale1[2] = (gdraw->vx - gdraw->tx0 + gdraw->tx0p) / (F32) texdesc.Width;
|
|
rescale1[3] = (gdraw->vy - gdraw->ty0 + gdraw->ty0p) / (F32) texdesc.Height;
|
|
} else {
|
|
D3D1X_(BOX) box = { 0,0,0,0,0,1 };
|
|
S32 dx = 0, dy = 0;
|
|
blend_tex = get_color_rendertarget(stats);
|
|
|
|
if (gdraw->cur != gdraw->frame)
|
|
box.right=gdraw->tpw, box.bottom=gdraw->tph;
|
|
else {
|
|
box.left=gdraw->vx, box.top=gdraw->vy, box.right=gdraw->vx+gdraw->tw, box.bottom=gdraw->vy+gdraw->th;
|
|
dx = gdraw->tx0 - gdraw->tx0p;
|
|
dy = gdraw->ty0 - gdraw->ty0p;
|
|
}
|
|
|
|
d3d->CopySubresourceRegion(blend_tex->handle.tex.d3d, 0, dx, dy, 0,
|
|
cur_rt_rsrc, 0, &box);
|
|
|
|
stats->nonzero_flags |= GDRAW_STATS_blits;
|
|
stats->num_blits += 1;
|
|
stats->num_blit_pixels += (box.right - box.left) * (box.bottom - box.top);
|
|
|
|
set_texture(1, (GDrawTexture *) blend_tex, false, GDRAW_WRAP_clamp);
|
|
}
|
|
|
|
cur_rt_rsrc->Release();
|
|
}
|
|
|
|
if (!set_renderstate_full(GDRAW_vformat_v2tc2, r, stats, rescale1))
|
|
return;
|
|
|
|
do_screen_quad(&s, tc, stats);
|
|
tag_resources(r->tex[0], r->tex[1]);
|
|
if (blend_tex)
|
|
gdraw_FreeTexture((GDrawTexture *) blend_tex, 0, stats);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Shaders and state
|
|
//
|
|
|
|
#include GDRAW_SHADER_FILE
|
|
|
|
static void destroy_shader(ProgramWithCachedVariableLocations *p)
|
|
{
|
|
if (p->pshader) {
|
|
p->pshader->Release();
|
|
p->pshader = NULL;
|
|
}
|
|
}
|
|
|
|
static ID3D1X(Buffer) *create_dynamic_buffer(U32 size, U32 bind)
|
|
{
|
|
D3D1X_(BUFFER_DESC) desc = { size, D3D1X_(USAGE_DYNAMIC), bind, D3D1X_(CPU_ACCESS_WRITE), 0 };
|
|
ID3D1X(Buffer) *buf = NULL;
|
|
HRESULT hr = gdraw->d3d_device->CreateBuffer(&desc, NULL, &buf);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateBuffer", " creating dynamic vertex buffer");
|
|
buf = NULL;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
static void init_dyn_buffer(DynBuffer *buf, U32 size, U32 bind)
|
|
{
|
|
buf->buffer = create_dynamic_buffer(size, bind);
|
|
buf->size = size;
|
|
buf->write_pos = 0;
|
|
buf->alloc_pos = 0;
|
|
}
|
|
|
|
// These two functions are implemented by the D3D10- respectively D3D11-specific part.
|
|
static void create_pixel_shader(ProgramWithCachedVariableLocations *p, ProgramWithCachedVariableLocations *src);
|
|
static void create_vertex_shader(ProgramWithCachedVariableLocations *p, ProgramWithCachedVariableLocations *src);
|
|
|
|
static void create_all_shaders_and_state(void)
|
|
{
|
|
ID3D1X(Device) *d3d = gdraw->d3d_device;
|
|
HRESULT hr;
|
|
S32 i, j;
|
|
|
|
for (i=0; i < GDRAW_TEXTURE__count*3; ++i) create_pixel_shader(&gdraw->fprog[0][i], pshader_basic_arr + i);
|
|
for (i=0; i < GDRAW_BLENDSPECIAL__count; ++i) create_pixel_shader(&gdraw->exceptional_blend[i], pshader_exceptional_blend_arr + i);
|
|
for (i=0; i < 32; ++i) create_pixel_shader(&gdraw->filter_prog[0][i], pshader_filter_arr + i);
|
|
for (i=0; i < MAX_TAPS+1; ++i) create_pixel_shader(&gdraw->blur_prog[i], pshader_blur_arr + i);
|
|
create_pixel_shader(&gdraw->colormatrix, pshader_color_matrix_arr);
|
|
create_pixel_shader(&gdraw->clear_ps, pshader_manual_clear_arr);
|
|
|
|
for (i=0; i < GDRAW_vformat__basic_count; i++) {
|
|
ProgramWithCachedVariableLocations *vsh = vshader_vsd3d10_arr + i;
|
|
|
|
create_vertex_shader(&gdraw->vert[i], vsh);
|
|
HRESULT hr = d3d->CreateInputLayout(vformats[i].desc, vformats[i].nelem, vsh->bytecode, vsh->size, &gdraw->inlayout[i]);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateInputLayout", "");
|
|
gdraw->inlayout[i] = NULL;
|
|
}
|
|
}
|
|
|
|
// create rasterizer state setups
|
|
for (i=0; i < 2; ++i) {
|
|
D3D1X_(RASTERIZER_DESC) raster_desc = { D3D1X_(FILL_SOLID), D3D1X_(CULL_NONE), FALSE, 0, 0.0f, 0.0f, TRUE, TRUE, FALSE, FALSE };
|
|
raster_desc.MultisampleEnable = i;
|
|
hr = d3d->CreateRasterizerState(&raster_desc, &gdraw->raster_state[i]);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateRasterizerState", "");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// create sampler state setups
|
|
static const D3D1X_(TEXTURE_ADDRESS_MODE) addrmode[ASSERT_COUNT(GDRAW_WRAP__count, 4)] = {
|
|
D3D1X_(TEXTURE_ADDRESS_CLAMP), // GDRAW_WRAP_clamp
|
|
D3D1X_(TEXTURE_ADDRESS_WRAP), // GDRAW_WRAP_repeat
|
|
D3D1X_(TEXTURE_ADDRESS_MIRROR), // GDRAW_WRAP_mirror
|
|
D3D1X_(TEXTURE_ADDRESS_CLAMP), // GDRAW_WRAP_clamp_to_border (unused for this renderer)
|
|
};
|
|
|
|
for (i=0; i < 2; ++i) {
|
|
for (j=0; j < GDRAW_WRAP__count; ++j) {
|
|
D3D1X_(SAMPLER_DESC) sampler_desc;
|
|
memset(&sampler_desc, 0, sizeof(sampler_desc));
|
|
sampler_desc.Filter = i ? D3D1X_(FILTER_MIN_LINEAR_MAG_MIP_POINT) : D3D1X_(FILTER_MIN_MAG_MIP_LINEAR);
|
|
sampler_desc.AddressU = addrmode[j];
|
|
sampler_desc.AddressV = addrmode[j];
|
|
sampler_desc.AddressW = D3D1X_(TEXTURE_ADDRESS_CLAMP);
|
|
sampler_desc.MaxAnisotropy = 1;
|
|
sampler_desc.MaxLOD = D3D1X_(FLOAT32_MAX);
|
|
hr = d3d->CreateSamplerState(&sampler_desc, &gdraw->sampler_state[i][j]);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateSamplerState", "");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// create blend stage setups
|
|
static struct blendspec {
|
|
BOOL blend;
|
|
D3D1X_(BLEND) src;
|
|
D3D1X_(BLEND) dst;
|
|
} blends[ASSERT_COUNT(GDRAW_BLEND__count, 6)] = {
|
|
FALSE, D3D1X_(BLEND_ONE), D3D1X_(BLEND_ZERO), // GDRAW_BLEND_none
|
|
TRUE, D3D1X_(BLEND_ONE), D3D1X_(BLEND_INV_SRC_ALPHA), // GDRAW_BLEND_alpha
|
|
TRUE, D3D1X_(BLEND_DEST_COLOR), D3D1X_(BLEND_INV_SRC_ALPHA), // GDRAW_BLEND_multiply
|
|
TRUE, D3D1X_(BLEND_ONE), D3D1X_(BLEND_ONE), // GDRAW_BLEND_add
|
|
|
|
FALSE, D3D1X_(BLEND_ONE), D3D1X_(BLEND_ZERO), // GDRAW_BLEND_filter
|
|
FALSE, D3D1X_(BLEND_ONE), D3D1X_(BLEND_ZERO), // GDRAW_BLEND_special
|
|
};
|
|
|
|
for (i=0; i < GDRAW_BLEND__count; ++i) {
|
|
gdraw->blend_state[i] = create_blend_state(d3d, blends[i].blend, blends[i].src, blends[i].dst);
|
|
if (!gdraw->blend_state[i])
|
|
return;
|
|
}
|
|
|
|
D3D1X_(BLEND_DESC) blend_desc;
|
|
memset(&blend_desc, 0, sizeof(blend_desc));
|
|
hr = d3d->CreateBlendState(&blend_desc, &gdraw->blend_no_color_write);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateBlendState", "");
|
|
return;
|
|
}
|
|
|
|
// create depth/stencil setups
|
|
for (i=0; i < 2; ++i) {
|
|
for (j=0; j < 2; ++j) {
|
|
D3D1X_(DEPTH_STENCIL_DESC) depth_desc;
|
|
memset(&depth_desc, 0, sizeof(depth_desc));
|
|
|
|
depth_desc.DepthEnable = (i || j);
|
|
depth_desc.DepthWriteMask = i ? D3D1X_(DEPTH_WRITE_MASK_ALL) : D3D1X_(DEPTH_WRITE_MASK_ZERO);
|
|
depth_desc.DepthFunc = j ? D3D1X_(COMPARISON_LESS) : D3D1X_(COMPARISON_ALWAYS);
|
|
depth_desc.StencilEnable = FALSE;
|
|
|
|
hr = d3d->CreateDepthStencilState(&depth_desc, &gdraw->depth_state[i][j]);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateDepthStencilState", "");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// constant buffers
|
|
gdraw->cb_vertex = create_dynamic_buffer(sizeof(VertexVars), D3D1X_(BIND_CONSTANT_BUFFER));
|
|
gdraw->cb_ps_common = create_dynamic_buffer(sizeof(PixelCommonVars), D3D1X_(BIND_CONSTANT_BUFFER));
|
|
gdraw->cb_filter = create_dynamic_buffer(sizeof(PixelParaFilter), D3D1X_(BIND_CONSTANT_BUFFER));
|
|
gdraw->cb_colormatrix = create_dynamic_buffer(sizeof(PixelParaColorMatrix), D3D1X_(BIND_CONSTANT_BUFFER));
|
|
gdraw->cb_blur = create_dynamic_buffer(sizeof(PixelParaBlur), D3D1X_(BIND_CONSTANT_BUFFER));
|
|
|
|
// quad index buffer
|
|
assert(QUAD_IB_COUNT * 4 < 65535); // can't use more; we have 16-bit index buffers and 0xffff = primitive cut index
|
|
U16 *inds = (U16 *) IggyGDrawMalloc(QUAD_IB_COUNT * 6 * sizeof(U16));
|
|
if (inds) {
|
|
D3D1X_(BUFFER_DESC) bufdesc = { };
|
|
D3D1X_(SUBRESOURCE_DATA) data = { inds, 0, 0 };
|
|
|
|
bufdesc.ByteWidth = QUAD_IB_COUNT * 6 * sizeof(U16);
|
|
bufdesc.Usage = D3D1X_(USAGE_IMMUTABLE);
|
|
bufdesc.BindFlags = D3D1X_(BIND_INDEX_BUFFER);
|
|
|
|
for (U16 i=0; i < QUAD_IB_COUNT; i++) {
|
|
inds[i*6 + 0] = i*4 + 0;
|
|
inds[i*6 + 1] = i*4 + 1;
|
|
inds[i*6 + 2] = i*4 + 2;
|
|
inds[i*6 + 3] = i*4 + 0;
|
|
inds[i*6 + 4] = i*4 + 2;
|
|
inds[i*6 + 5] = i*4 + 3;
|
|
}
|
|
|
|
hr = gdraw->d3d_device->CreateBuffer(&bufdesc, &data, &gdraw->quad_ib);
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, "CreateBuffer", " for constants");
|
|
gdraw->quad_ib = NULL;
|
|
}
|
|
IggyGDrawFree(inds);
|
|
} else
|
|
gdraw->quad_ib = NULL;
|
|
}
|
|
|
|
static void destroy_all_shaders_and_state()
|
|
{
|
|
S32 i;
|
|
|
|
for (i=0; i < GDRAW_TEXTURE__count*3; ++i) destroy_shader(&gdraw->fprog[0][i]);
|
|
for (i=0; i < GDRAW_BLENDSPECIAL__count; ++i) destroy_shader(&gdraw->exceptional_blend[i]);
|
|
for (i=0; i < 32; ++i) destroy_shader(&gdraw->filter_prog[0][i]);
|
|
for (i=0; i < MAX_TAPS+1; ++i) destroy_shader(&gdraw->blur_prog[i]);
|
|
destroy_shader(&gdraw->colormatrix);
|
|
destroy_shader(&gdraw->clear_ps);
|
|
|
|
for (i=0; i < GDRAW_vformat__basic_count; i++) {
|
|
safe_release(gdraw->inlayout[i]);
|
|
destroy_shader(&gdraw->vert[i]);
|
|
}
|
|
|
|
for (i=0; i < 2; ++i) safe_release(gdraw->raster_state[i]);
|
|
for (i=0; i < GDRAW_WRAP__count*2; ++i) safe_release(gdraw->sampler_state[0][i]);
|
|
for (i=0; i < GDRAW_BLEND__count; ++i) safe_release(gdraw->blend_state[i]);
|
|
for (i=0; i < 2*2; ++i) safe_release(gdraw->depth_state[0][i]);
|
|
|
|
safe_release(gdraw->blend_no_color_write);
|
|
|
|
safe_release(gdraw->cb_vertex);
|
|
safe_release(gdraw->cb_ps_common);
|
|
safe_release(gdraw->cb_filter);
|
|
safe_release(gdraw->cb_colormatrix);
|
|
safe_release(gdraw->cb_blur);
|
|
|
|
safe_release(gdraw->quad_ib);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Create and tear-down the state
|
|
//
|
|
|
|
typedef struct
|
|
{
|
|
S32 num_handles;
|
|
S32 num_bytes;
|
|
} GDrawResourceLimit;
|
|
|
|
// These are the defaults limits used by GDraw unless the user specifies something else.
|
|
static GDrawResourceLimit gdraw_limits[GDRAW_D3D1X_(RESOURCE__count)] = {
|
|
MAX_RENDER_STACK_DEPTH + 1, 16*1024*1024, // RESOURCE_rendertarget
|
|
500, 16*1024*1024, // RESOURCE_texture
|
|
1000, 2*1024*1024, // RESOURCE_vertexbuffer
|
|
0, 256*1024, // RESOURCE_dynbuffer
|
|
};
|
|
|
|
static GDrawHandleCache *make_handle_cache(gdraw_resourcetype type)
|
|
{
|
|
S32 num_handles = gdraw_limits[type].num_handles;
|
|
S32 num_bytes = gdraw_limits[type].num_bytes;
|
|
GDrawHandleCache *cache = (GDrawHandleCache *) IggyGDrawMalloc(sizeof(GDrawHandleCache) + (num_handles - 1) * sizeof(GDrawHandle));
|
|
if (cache) {
|
|
gdraw_HandleCacheInit(cache, num_handles, num_bytes);
|
|
cache->is_vertex = (type == GDRAW_D3D1X_(RESOURCE_vertexbuffer));
|
|
}
|
|
|
|
return cache;
|
|
}
|
|
|
|
static void free_gdraw()
|
|
{
|
|
if (!gdraw) return;
|
|
if (gdraw->texturecache) IggyGDrawFree(gdraw->texturecache);
|
|
if (gdraw->vbufcache) IggyGDrawFree(gdraw->vbufcache);
|
|
IggyGDrawFree(gdraw);
|
|
gdraw = NULL;
|
|
}
|
|
|
|
static bool alloc_dynbuffer(U32 size)
|
|
{
|
|
// specified input size is vertex buffer size. determine sensible size for the
|
|
// corresponding index buffer. iggy always uses 16-bit indices and has three
|
|
// primary types of geometry it sends:
|
|
//
|
|
// 1. filled polygons. these are triangulated simple polygons and thus have
|
|
// roughly as many triangles as they have vertices. they use either 8- or
|
|
// 16-byte vertex formats; this makes a worst case of 6 bytes of indices
|
|
// for every 8 bytes of vertex data.
|
|
// 2. strokes and edge antialiasing. they use a 16-byte vertex format and
|
|
// worst-case write a "double quadstrip" which has 4 triangles for every
|
|
// 3 vertices, which means 24 bytes of index data for every 48 bytes
|
|
// of vertex data.
|
|
// 3. textured quads. they use a 16-byte vertex format, have exactly 2
|
|
// triangles for every 4 vertices, and use either a static index buffer
|
|
// (quad_ib) or a single triangle strip, so for our purposes they need no
|
|
// space to store indices at all.
|
|
//
|
|
// 1) argues for allocating index buffers at 3/4 the size of the corresponding
|
|
// vertex buffer, while 2) and 3) need 1/2 the size of the vertex buffer or less.
|
|
// 2) and 3) are the most common types of vertex data, while 1) is used only for
|
|
// morphed shapes and in certain cases when the RESOURCE_vertexbuffer pool is full.
|
|
// we just play it safe anyway and make sure we size the IB large enough to cover
|
|
// the worst case for 1). this is conservative, but it probably doesn't matter much.
|
|
U32 ibsize = (size * 3) / 4;
|
|
|
|
init_dyn_buffer(&gdraw->dyn_vb, size, D3D1X_(BIND_VERTEX_BUFFER));
|
|
init_dyn_buffer(&gdraw->dyn_ib, ibsize, D3D1X_(BIND_INDEX_BUFFER));
|
|
|
|
gdraw->max_quad_vert_count = RR_MIN(size / sizeof(gswf_vertex_xyst), QUAD_IB_COUNT * 4);
|
|
gdraw->max_quad_vert_count &= ~3; // must be multiple of four
|
|
|
|
return gdraw->dyn_vb.buffer != NULL && gdraw->dyn_ib.buffer != NULL;
|
|
}
|
|
|
|
int gdraw_D3D1X_(SetResourceLimits)(gdraw_resourcetype type, S32 num_handles, S32 num_bytes)
|
|
{
|
|
GDrawStats stats={0};
|
|
|
|
if (type == GDRAW_D3D1X_(RESOURCE_rendertarget)) // RT count is small and space is preallocated
|
|
num_handles = MAX_RENDER_STACK_DEPTH + 1;
|
|
|
|
assert(type >= GDRAW_D3D1X_(RESOURCE_rendertarget) && type < GDRAW_D3D1X_(RESOURCE__count));
|
|
assert(num_handles >= 0);
|
|
assert(num_bytes >= 0);
|
|
|
|
// nothing to do if the values are unchanged
|
|
if (gdraw_limits[type].num_handles == num_handles &&
|
|
gdraw_limits[type].num_bytes == num_bytes)
|
|
return 1;
|
|
|
|
gdraw_limits[type].num_handles = num_handles;
|
|
gdraw_limits[type].num_bytes = num_bytes;
|
|
|
|
// if no gdraw context created, there's nothing to worry about
|
|
if (!gdraw)
|
|
return 1;
|
|
|
|
// resize the appropriate pool
|
|
switch (type) {
|
|
case GDRAW_D3D1X_(RESOURCE_rendertarget):
|
|
flush_rendertargets(&stats);
|
|
gdraw_HandleCacheInit(&gdraw->rendertargets, num_handles, num_bytes);
|
|
return 1;
|
|
|
|
case GDRAW_D3D1X_(RESOURCE_texture):
|
|
if (gdraw->texturecache) {
|
|
gdraw_res_flush(gdraw->texturecache, &stats);
|
|
IggyGDrawFree(gdraw->texturecache);
|
|
}
|
|
gdraw->texturecache = make_handle_cache(GDRAW_D3D1X_(RESOURCE_texture));
|
|
return gdraw->texturecache != NULL;
|
|
|
|
case GDRAW_D3D1X_(RESOURCE_vertexbuffer):
|
|
if (gdraw->vbufcache) {
|
|
gdraw_res_flush(gdraw->vbufcache, &stats);
|
|
IggyGDrawFree(gdraw->vbufcache);
|
|
}
|
|
gdraw->vbufcache = make_handle_cache(GDRAW_D3D1X_(RESOURCE_vertexbuffer));
|
|
return gdraw->vbufcache != NULL;
|
|
|
|
case GDRAW_D3D1X_(RESOURCE_dynbuffer):
|
|
unbind_resources();
|
|
safe_release(gdraw->dyn_vb.buffer);
|
|
safe_release(gdraw->dyn_ib.buffer);
|
|
return alloc_dynbuffer(num_bytes);
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static GDrawFunctions *create_context(ID3D1XDevice *dev, ID3D1XContext *ctx, S32 w, S32 h)
|
|
{
|
|
gdraw = (GDraw *) IggyGDrawMalloc(sizeof(*gdraw));
|
|
if (!gdraw) return NULL;
|
|
|
|
memset(gdraw, 0, sizeof(*gdraw));
|
|
|
|
gdraw->frametex_width = w;
|
|
gdraw->frametex_height = h;
|
|
gdraw->d3d_device = dev;
|
|
gdraw->d3d_context = ctx;
|
|
|
|
gdraw->texturecache = make_handle_cache(GDRAW_D3D1X_(RESOURCE_texture));
|
|
gdraw->vbufcache = make_handle_cache(GDRAW_D3D1X_(RESOURCE_vertexbuffer));
|
|
gdraw_HandleCacheInit(&gdraw->rendertargets, gdraw_limits[GDRAW_D3D1X_(RESOURCE_rendertarget)].num_handles, gdraw_limits[GDRAW_D3D1X_(RESOURCE_rendertarget)].num_bytes);
|
|
|
|
if (!gdraw->texturecache || !gdraw->vbufcache || !alloc_dynbuffer(gdraw_limits[GDRAW_D3D1X_(RESOURCE_dynbuffer)].num_bytes)) {
|
|
free_gdraw();
|
|
return NULL;
|
|
}
|
|
|
|
create_all_shaders_and_state();
|
|
|
|
gdraw_funcs.SetViewSizeAndWorldScale = gdraw_SetViewSizeAndWorldScale;
|
|
gdraw_funcs.GetInfo = gdraw_GetInfo;
|
|
|
|
gdraw_funcs.DescribeTexture = gdraw_DescribeTexture;
|
|
gdraw_funcs.DescribeVertexBuffer = gdraw_DescribeVertexBuffer;
|
|
|
|
gdraw_funcs.RenderingBegin = gdraw_RenderingBegin;
|
|
gdraw_funcs.RenderingEnd = gdraw_RenderingEnd;
|
|
gdraw_funcs.RenderTileBegin = gdraw_RenderTileBegin;
|
|
gdraw_funcs.RenderTileEnd = gdraw_RenderTileEnd;
|
|
|
|
gdraw_funcs.TextureDrawBufferBegin = gdraw_TextureDrawBufferBegin;
|
|
gdraw_funcs.TextureDrawBufferEnd = gdraw_TextureDrawBufferEnd;
|
|
|
|
gdraw_funcs.DrawIndexedTriangles = gdraw_DrawIndexedTriangles;
|
|
gdraw_funcs.FilterQuad = gdraw_FilterQuad;
|
|
|
|
gdraw_funcs.SetAntialiasTexture = gdraw_SetAntialiasTexture;
|
|
|
|
gdraw_funcs.ClearStencilBits = gdraw_ClearStencilBits;
|
|
gdraw_funcs.ClearID = gdraw_ClearID;
|
|
|
|
gdraw_funcs.MakeTextureBegin = gdraw_MakeTextureBegin;
|
|
gdraw_funcs.MakeTextureMore = gdraw_MakeTextureMore;
|
|
gdraw_funcs.MakeTextureEnd = gdraw_MakeTextureEnd;
|
|
|
|
gdraw_funcs.UpdateTextureBegin = gdraw_UpdateTextureBegin;
|
|
gdraw_funcs.UpdateTextureRect = gdraw_UpdateTextureRect;
|
|
gdraw_funcs.UpdateTextureEnd = gdraw_UpdateTextureEnd;
|
|
|
|
gdraw_funcs.FreeTexture = gdraw_FreeTexture;
|
|
gdraw_funcs.TryToLockTexture = gdraw_TryToLockTexture;
|
|
|
|
gdraw_funcs.MakeTextureFromResource = (gdraw_make_texture_from_resource *) gdraw_D3D1X_(MakeTextureFromResource);
|
|
gdraw_funcs.FreeTextureFromResource = gdraw_D3D1X_(DestroyTextureFromResource);
|
|
|
|
gdraw_funcs.MakeVertexBufferBegin = gdraw_MakeVertexBufferBegin;
|
|
gdraw_funcs.MakeVertexBufferMore = gdraw_MakeVertexBufferMore;
|
|
gdraw_funcs.MakeVertexBufferEnd = gdraw_MakeVertexBufferEnd;
|
|
gdraw_funcs.TryToLockVertexBuffer = gdraw_TryLockVertexBuffer;
|
|
gdraw_funcs.FreeVertexBuffer = gdraw_FreeVertexBuffer;
|
|
|
|
gdraw_funcs.UnlockHandles = gdraw_UnlockHandles;
|
|
gdraw_funcs.SetTextureUniqueID = gdraw_SetTextureUniqueID;
|
|
|
|
gdraw_funcs.Set3DTransform = gdraw_Set3DTransform;
|
|
|
|
return &gdraw_funcs;
|
|
}
|
|
|
|
void gdraw_D3D1X_(DestroyContext)(void)
|
|
{
|
|
if (gdraw && gdraw->d3d_device) {
|
|
GDrawStats stats={0};
|
|
clear_renderstate();
|
|
stencil_state_cache_clear();
|
|
destroy_all_shaders_and_state();
|
|
safe_release(gdraw->aa_tex);
|
|
safe_release(gdraw->aa_tex_view);
|
|
safe_release(gdraw->dyn_vb.buffer);
|
|
safe_release(gdraw->dyn_ib.buffer);
|
|
|
|
flush_rendertargets(&stats);
|
|
if (gdraw->texturecache) gdraw_res_flush(gdraw->texturecache, &stats);
|
|
if (gdraw->vbufcache) gdraw_res_flush(gdraw->vbufcache, &stats);
|
|
|
|
gdraw->d3d_device = NULL;
|
|
}
|
|
|
|
free_gdraw();
|
|
}
|
|
|
|
void gdraw_D3D1X_(SetErrorHandler)(void (__cdecl *error_handler)(HRESULT hr))
|
|
{
|
|
if (gdraw)
|
|
gdraw->error_handler = error_handler;
|
|
}
|
|
|
|
void gdraw_D3D1X_(PreReset)(void)
|
|
{
|
|
if (!gdraw) return;
|
|
|
|
GDrawStats stats={0};
|
|
flush_rendertargets(&stats);
|
|
|
|
// we may end up resizing the frame buffer
|
|
gdraw->frametex_width = 0;
|
|
gdraw->frametex_height = 0;
|
|
}
|
|
|
|
void gdraw_D3D1X_(PostReset)(void)
|
|
{
|
|
// maybe re-create rendertargets right now?
|
|
}
|
|
|
|
void RADLINK gdraw_D3D1X_(BeginCustomDraw)(IggyCustomDrawCallbackRegion * region, F32 mat[4][4])
|
|
{
|
|
clear_renderstate();
|
|
gdraw_GetObjectSpaceMatrix(mat[0], region->o2w, gdraw->projection, 0, 0);
|
|
}
|
|
|
|
void RADLINK gdraw_D3D1X_(BeginCustomDraw_4J)(IggyCustomDrawCallbackRegion * region, F32 mat[16])
|
|
{
|
|
clear_renderstate();
|
|
gdraw_GetObjectSpaceMatrix(mat, region->o2w, gdraw->projection, 0, 0);
|
|
}
|
|
|
|
void RADLINK gdraw_D3D1X_(CalculateCustomDraw_4J)(IggyCustomDrawCallbackRegion * region, F32 mat[16])
|
|
{
|
|
gdraw_GetObjectSpaceMatrix(mat, region->o2w, gdraw->projection, 0, 0);
|
|
}
|
|
|
|
void RADLINK gdraw_D3D1X_(EndCustomDraw)(IggyCustomDrawCallbackRegion * /*region*/)
|
|
{
|
|
GDrawStats stats={};
|
|
set_common_renderstate();
|
|
set_viewport();
|
|
set_render_target(&stats);
|
|
}
|
|
|
|
void RADLINK gdraw_D3D1X_(GetResourceUsageStats)(gdraw_resourcetype type, S32 *handles_used, S32 *bytes_used)
|
|
{
|
|
GDrawHandleCache *cache;
|
|
|
|
switch (type) {
|
|
case GDRAW_D3D1X_(RESOURCE_rendertarget): cache = &gdraw->rendertargets; break;
|
|
case GDRAW_D3D1X_(RESOURCE_texture): cache = gdraw->texturecache; break;
|
|
case GDRAW_D3D1X_(RESOURCE_vertexbuffer): cache = gdraw->vbufcache; break;
|
|
case GDRAW_D3D1X_(RESOURCE_dynbuffer): *handles_used = 0; *bytes_used = gdraw->last_dyn_maxalloc; return;
|
|
default: cache = NULL; break;
|
|
}
|
|
|
|
*handles_used = *bytes_used = 0;
|
|
|
|
if (cache) {
|
|
S32 i;
|
|
U64 frame = gdraw->frame_counter;
|
|
|
|
for (i=0; i < cache->max_handles; ++i)
|
|
if (cache->handle[i].bytes && cache->handle[i].owner && cache->handle[i].fence.value == frame) {
|
|
*handles_used += 1;
|
|
*bytes_used += cache->handle[i].bytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
static S32 num_pixels(S32 w, S32 h, S32 mipmaps)
|
|
{
|
|
S32 k, pixels=0;
|
|
for (k=0; k < mipmaps; ++k) {
|
|
pixels += w*h*2;
|
|
w = (w>>1); w += !w;
|
|
h = (h>>1); h += !h;
|
|
}
|
|
return pixels;
|
|
}
|
|
|
|
GDrawTexture * RADLINK gdraw_D3D1X_(MakeTextureFromResource)(U8 *resource_file, S32 /*len*/, IggyFileTextureRaw *texture)
|
|
{
|
|
char *failed_call="";
|
|
U8 *free_data = 0;
|
|
GDrawTexture *t=0;
|
|
S32 width, height, mipmaps, size, blk;
|
|
ID3D1X(Texture2D) *tex=0;
|
|
ID3D1X(ShaderResourceView) *view=0;
|
|
|
|
DXGI_FORMAT d3dfmt;
|
|
D3D1X_(SUBRESOURCE_DATA) mipdata[24] = { 0 };
|
|
S32 k;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
width = texture->w;
|
|
height = texture->h;
|
|
mipmaps = texture->mipmaps;
|
|
blk = 1;
|
|
|
|
D3D1X_(TEXTURE2D_DESC) desc = { width, height, mipmaps, 1, DXGI_FORMAT_UNKNOWN, { 1, 0 },
|
|
D3D1X_(USAGE_IMMUTABLE), D3D1X_(BIND_SHADER_RESOURCE), 0, 0 };
|
|
|
|
switch (texture->format) {
|
|
case IFT_FORMAT_rgba_8888 : size= 4; d3dfmt = DXGI_FORMAT_R8G8B8A8_UNORM; break;
|
|
case IFT_FORMAT_DXT1 : size= 8; d3dfmt = DXGI_FORMAT_BC1_UNORM; blk = 4; break;
|
|
case IFT_FORMAT_DXT3 : size=16; d3dfmt = DXGI_FORMAT_BC2_UNORM; blk = 4; break;
|
|
case IFT_FORMAT_DXT5 : size=16; d3dfmt = DXGI_FORMAT_BC3_UNORM; blk = 4; break;
|
|
default: {
|
|
IggyGDrawSendWarning(NULL, "GDraw .iggytex raw texture format %d not supported by hardware", texture->format);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
desc.Format = d3dfmt;
|
|
|
|
U8 *data = resource_file + texture->file_offset;
|
|
|
|
if (texture->format == IFT_FORMAT_i_8 || texture->format == IFT_FORMAT_i_4) {
|
|
// convert from intensity to luma+alpha
|
|
S32 i;
|
|
S32 total_size = 2 * num_pixels(width,height,mipmaps);
|
|
|
|
free_data = (U8 *) IggyGDrawMalloc(total_size);
|
|
if (!free_data) {
|
|
IggyGDrawSendWarning(NULL, "GDraw out of memory to store texture data to pass to D3D for %d x %d texture", width, height);
|
|
goto done;
|
|
}
|
|
|
|
U8 *cur = free_data;
|
|
|
|
for (k=0; k < mipmaps; ++k) {
|
|
S32 w = RR_MAX(width >> k, 1);
|
|
S32 h = RR_MAX(height >> k, 1);
|
|
for (i=0; i < w*h; ++i) {
|
|
cur[0] = cur[1] = *data++;
|
|
cur += 2;
|
|
}
|
|
}
|
|
data = free_data;
|
|
}
|
|
|
|
for (k=0; k < mipmaps; ++k) {
|
|
S32 w = RR_MAX(width >> k, 1);
|
|
S32 h = RR_MAX(height >> k, 1);
|
|
S32 blkw = (w + blk-1) / blk;
|
|
S32 blkh = (h + blk-1) / blk;
|
|
|
|
mipdata[k].pSysMem = data;
|
|
mipdata[k].SysMemPitch = blkw * size;
|
|
data += blkw * blkh * size;
|
|
}
|
|
|
|
failed_call = "CreateTexture2D";
|
|
hr = gdraw->d3d_device->CreateTexture2D(&desc, mipdata, &tex);
|
|
if (FAILED(hr)) goto done;
|
|
|
|
failed_call = "CreateShaderResourceView for texture creation";
|
|
hr = gdraw->d3d_device->CreateShaderResourceView(tex, NULL, &view);
|
|
if (FAILED(hr)) goto done;
|
|
|
|
t = gdraw_D3D1X_(WrappedTextureCreate)(view);
|
|
|
|
done:
|
|
if (FAILED(hr)) {
|
|
report_d3d_error(hr, failed_call, "");
|
|
}
|
|
|
|
if (free_data)
|
|
IggyGDrawFree(free_data);
|
|
|
|
if (!t) {
|
|
if (view)
|
|
view->Release();
|
|
if (tex)
|
|
tex->Release();
|
|
} else {
|
|
((GDrawHandle *) t)->handle.tex.d3d = tex;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
void RADLINK gdraw_D3D1X_(DestroyTextureFromResource)(GDrawTexture *tex)
|
|
{
|
|
GDrawHandle *h = (GDrawHandle *) tex;
|
|
safe_release(h->handle.tex.d3d_view);
|
|
safe_release(h->handle.tex.d3d);
|
|
gdraw_D3D1X_(WrappedTextureDestroy)(tex);
|
|
}
|
|
|