direct封装api
struct _gpu_t : public gpu_t
{
//d3d
d3d_t d3d;
byte4 ordinal;
//identifier
D3DADAPTER_IDENTIFIER9 identifier;
//hardware caps
gpu_caps_t hardware_caps;
//d3d device
d3ddevice_t device;
//working caps
gpu_caps_t working_caps;
//pipeline initialization parameters
gpu_creation_parameters_t cur_cp;
gpu_presentation_parameters_t cur_pp;
//decl pool
map<byte4, d3ddecl_t> decl_map; //fvf -> decl
//dynamic vertex buffer
d3dvb_t dynvb;
byte4 dynvb_size;
byte4 dynvb_used_bytes;
bool dynvb_broken;
//global render states
matrix mat_view;
matrix mat_proj;
matrix mat_viewproj;
matrix mat_viewproj_transpose;
matrix mat_viewprojviewport;
D3DVIEWPORT9 cur_viewport;
byte4 viewproj_vsconst_reg; // -1 for unmapped
vector<global_vsconst_def_t> global_vsconst_vec;
//static render states
vector< static_render_states_t > srs_vec;
vector< vector<transition_info_t> > transition_table;
byte4 cur_srs; //0 for the default one
//dynamic render states
dynamic_render_states_t cur_drs;
//init / clear (called by d3d init()/shutdown() only)
bool init(d3d_t _d3d, byte4 _ordinal); //fail if the HAL-device is not-available
void clear() {} //nothing to clear
//identifier
const D3DADAPTER_IDENTIFIER9* get_identifier() {return &identifier;}
//current display mode
void get_current_display_mode(D3DDISPLAYMODE* mode) {d3d->GetAdapterDisplayMode(ordinal, mode);}
//static caps
const gpu_caps_t* get_hardware_caps() {return &hardware_caps;}
gpu_vertex_processing_method_t calc_vertex_processing_level();
byte4 build_valid_presentation_parameter_combos(BOOL multisample, buffer_t* out);
//pipeline control
bool turn_on(const gpu_creation_parameters_t* cp, const gpu_presentation_parameters_t* pp);
void turn_off();
bool reset(const gpu_presentation_parameters_t* pp);
bool begin_scene() {return SUCCEEDED(device->BeginScene());}
bool end_scene() {return SUCCEEDED(device->EndScene());}
bool present();
bool test_cooperative_level();
//creation parameters
const gpu_creation_parameters_t* get_creation_parameters() {return &cur_cp;}
const gpu_presentation_parameters_t* get_presentation_parameters() {return &cur_pp;}
//dynamic caps
const gpu_caps_t* get_working_caps() {return &working_caps;}
byte4 build_valid_render_target_formats(buffer_t* out);
byte4 build_valid_render_target_texture_formats(buffer_t* out);
byte4 build_valid_offscr_texture_formats(buffer_t* out);
byte4 build_valid_downsampling_target_formats(buffer_t* out);
byte4 build_valid_downsampling_source_formats(D3DFORMAT target_format, buffer_t* out);
//static objects
bool create_vertex_buffer(byte4 length, byte4 usage, byte4 fvf, D3DPOOL pool, d3dvb_t* out) {return SUCCEEDED(device->CreateVertexBuffer(length, usage, fvf, pool, out, 0));} //创建定点buffer
bool create_index_buffer(byte4 length, byte4 usage, D3DFORMAT format, D3DPOOL pool, d3dib_t* out) {return SUCCEEDED(device->CreateIndexBuffer(length, usage, format, pool, out, 0));} //创建索引buffer
bool create_offscr_surface(byte4 width, byte4 height, D3DFORMAT format, D3DPOOL pool, d3dsurface_t* out) {return SUCCEEDED(device->CreateOffscreenPlainSurface(width, height, format, pool, out, 0));}
bool create_render_target(byte4 width, byte4 height, D3DFORMAT format, D3DMULTISAMPLE_TYPE multisample_type, byte4 multisample_quality, bool lockable, d3dsurface_t* out) {return SUCCEEDED(device->CreateRenderTarget(width, height, format, multisample_type, multisample_quality, lockable, out, 0));} //创建纹理
bool create_texture(byte4 width, byte4 height, byte4 levels, byte4 usage, D3DFORMAT format, D3DPOOL pool, d3dtexture_t* out) {return SUCCEEDED(device->CreateTexture(width, height, levels, usage, format, pool, out, 0));}
bool create_vertex_shader(const byte4* bytecode, d3dvs_t* out) {return SUCCEEDED(device->CreateVertexShader(bytecode, out));}
//decl pool
bool create_vertex_declaration(byte4 fvf, d3ddecl_t* out);
//dynamic vertex buffer
void set_dynamic_vertex_buffer_size(byte4 size);
void* lock_dynamic_vertex_buffer(byte4 vertex_count, byte4 stride, byte4* start_vertex, d3dvb_t* vertex_buffer);
void unlock_dynamic_vertex_buffer();
//global render states
void set_view_matrix(const matrix* view);
void set_proj_matrix(const matrix* proj);
void set_viewport(const D3DVIEWPORT9* viewport);
const matrix* get_view_matrix() {return &mat_view;}
const matrix* get_proj_matrix() {return &mat_proj;}
const matrix* get_viewproj_matrix() {return &mat_viewproj;}
const matrix* get_viewproj_transpose_matrix() {return &mat_viewproj_transpose;}
const D3DVIEWPORT9* get_viewport() {return &cur_viewport;}
const matrix* get_viewprojviewport_matrix() {return &mat_viewprojviewport;}
void map_viewproj_to_vsconst(byte4 reg);
void set_global_vsconst(byte4 reg, const float* data, byte4 float4_count);
void set_render_target(d3dsurface_t rt) {device->SetRenderTarget(0, rt);}
void get_render_target(d3dsurface_t* rt) {device->GetRenderTarget(0, rt);}
//static render states
byte4 define_static_render_states(const static_render_states_t* in);
void set_static_render_states(byte4 id);
//dynamic render states
void set_software_vertex_processing(bool software);
void set_stream_source(d3dvb_t vertex_buffer, byte4 stride);
void set_indices(d3dib_t index_buffer);
void set_fvf(byte4 fvf);
void set_decl(d3ddecl_t decl);
void set_vertex_shader(d3dvs_t vertex_shader);
void set_vertex_shader_constants(byte4 start_register, const float* data, byte4 float4_count);
void set_vertex_blend(D3DVERTEXBLENDFLAGS vertex_blend);
void set_indexed_vertex_blend_enable(bool enable);
void set_world_matrix(byte4 index, const matrix* world_matrix);
void set_texture(d3dtexture_t texture);
void set_texture_factor(byte4 factor);
//draw
void draw_primitive(D3DPRIMITIVETYPE primitive_type, byte4 start_vertex, byte4 primitive_count) {device->DrawPrimitive(primitive_type, start_vertex, primitive_count);}
void draw_indexed_primitive(D3DPRIMITIVETYPE primitive_type, int base_vertex_index, byte4 min_vertex_index, byte4 num_vertices, byte4 start_index, byte4 primitive_count) {device->DrawIndexedPrimitive(primitive_type, base_vertex_index, min_vertex_index, num_vertices, start_index, primitive_count);}
//surface operations
bool clear_target(byte4 count, const D3DRECT* rects, byte4 flags, D3DCOLOR color, float z, byte4 stencil) {return SUCCEEDED(device->Clear(0, 0, flags, color, z, stencil));}
bool update_texture(d3dtexture_t src, d3dtexture_t dst) {return SUCCEEDED(device->UpdateTexture(src, dst));}
bool stretch_rect(d3dsurface_t src, const RECT* src_rect, d3dsurface_t dst, const RECT* dst_rect, D3DTEXTUREFILTERTYPE filter) {return SUCCEEDED(device->StretchRect(src, src_rect, dst, dst_rect, filter));}
bool color_fill(d3dsurface_t surface, const RECT* rect, D3DCOLOR color) {return SUCCEEDED(device->ColorFill(surface, rect, color));}
bool get_render_target_data(d3dsurface_t rt, d3dsurface_t dst) {return SUCCEEDED(device->GetRenderTargetData(rt, dst));}
bool get_front_buffer_data(d3dsurface_t dst) {return SUCCEEDED(device->GetFrontBufferData(0, dst));}
//d3d device exposed for D3DX lib
d3ddevice_t d3ddevice() {return device;}
//internal routines
void reflect_global_states();
void force_default_dynamic_states();
};
bool _gpu_t::init(d3d_t _d3d, byte4 _ordinal)
{
//check that whether this is a valid HAL device
D3DCAPS9 d3dcaps;
if(FAILED(_d3d->GetDeviceCaps(_ordinal, D3DDEVTYPE_HAL, &d3dcaps)))
return false;
//d3d
d3d = _d3d;
//adapter ordinal
ordinal = _ordinal;
//identifier
_d3d->GetAdapterIdentifier(_ordinal, 0, &identifier);
//hardware caps
translate_caps(&d3dcaps, &hardware_caps);
//device
device = 0;
//working caps defaults to hardware caps
translate_caps(&d3dcaps, &working_caps);
//cur_cp, cup_pp is undefined now
memset(&cur_cp, 0, sizeof(cur_cp));
memset(&cur_pp, 0, sizeof(cur_pp));
//no dynamic vertex buffer
dynvb = 0;
dynvb_size = 0;
dynvb_used_bytes = 0;
dynvb_broken = false;
//global states is undefined now
D3DXMatrixIdentity(&mat_view);
D3DXMatrixIdentity(&mat_proj);
D3DXMatrixIdentity(&mat_viewproj);
D3DXMatrixIdentity(&mat_viewproj_transpose);
D3DXMatrixIdentity(&mat_viewprojviewport);
memset(&cur_viewport, 0, sizeof(cur_viewport));
viewproj_vsconst_reg = -1;
//static states is undefined now
cur_srs = -1;
//dynamic states is undefined now
memset(&cur_drs, 0, sizeof(cur_drs));
return true;
}
gpu_vertex_processing_method_t _gpu_t::calc_vertex_processing_level()
{
D3DCAPS9 d3dcaps;
d3d->GetDeviceCaps(ordinal, D3DDEVTYPE_HAL, &d3dcaps);
return (d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ? ((d3dcaps.DevCaps & D3DDEVCAPS_PUREDEVICE) ? _gpu_vertex_processing_method_purehardware_ : _gpu_vertex_processing_method_hardware_) : _gpu_vertex_processing_method_software_;
}
byte4 _gpu_t::build_valid_presentation_parameter_combos(BOOL multisample, buffer_t* out)
{
//current display mode
D3DDISPLAYMODE cur_display_mode;
d3d->GetAdapterDisplayMode(ordinal, &cur_display_mode);
//build the set of all valid display_modes (ignore refresh-rate)
vector<D3DDISPLAYMODE> display_mode_vec;
display_mode_vec.reserve(128);
//ensure that the current display mode is in
display_mode_vec.push_back(cur_display_mode);
display_mode_vec[0].RefreshRate = 0; //ignore refresh-rate
const D3DFORMAT allowed_display_fmt_vec[] =
{
D3DFMT_X1R5G5B5,
D3DFMT_R5G6B5,
D3DFMT_X8R8G8B8,
D3DFMT_A2R10G10B10,
};
const byte4 allowed_display_fmt_count = sizeof(allowed_display_fmt_vec) / sizeof(allowed_display_fmt_vec[0]);
for(byte4 ifmt = 0; ifmt < allowed_display_fmt_count; ifmt++)
{
byte4 mode_count = d3d->GetAdapterModeCount(ordinal, allowed_display_fmt_vec[ifmt]);
for(byte4 imode = 0; imode < mode_count; imode++)
{
D3DDISPLAYMODE display_mode;
d3d->EnumAdapterModes(ordinal, allowed_display_fmt_vec[ifmt], imode, &display_mode);
//ignore refresh-rate
bool exist = false;
for(byte4 inow = 0; inow < display_mode_vec.size(); inow++)
{
if( display_mode_vec[inow].Format == display_mode.Format &&
display_mode_vec[inow].Width == display_mode.Width &&
display_mode_vec[inow].Height == display_mode.Height )
{
exist = true;
break;
}
}
if(!exist)
{
display_mode.RefreshRate = 0;
display_mode_vec.push_back(display_mode);
}
}
}
//build the set of all possible backbuffer formats
const D3DFORMAT bkbuf_fmt_vec[] =
{
D3DFMT_X1R5G5B5,
D3DFMT_A1R5G5B5,
D3DFMT_R5G6B5,
D3DFMT_X8R8G8B8,
D3DFMT_A8R8G8B8,
D3DFMT_A2R10G10B10,
};
const byte4 bkbuf_fmt_count = sizeof(bkbuf_fmt_vec) / sizeof(bkbuf_fmt_vec[0]);
//build the set of all possible depth-stencil formats (ignore lockable formats)
const D3DFORMAT ds_fmt_vec[] =
{
D3DFMT_D16,
D3DFMT_D15S1,
D3DFMT_D24X8,
D3DFMT_D24S8,
D3DFMT_D24X4S4,
D3DFMT_D32,
D3DFMT_D24FS8,
};
const byte4 ds_fmt_count = sizeof(ds_fmt_vec) / sizeof(ds_fmt_vec[0]);
//build the set of all windowed-fullscreen states
const bool windowed_vec[] = {true, false};
//build the set of all gdi-dialogbox states
const bool dialogbox_support_vec[] = {true, false};
//loop all the combos, filter out invalid ones, and collect the remains
vector<gpu_presentation_parameters_t> valid_pp_vec;
valid_pp_vec.reserve(1024);
gpu_presentation_parameters_t pp;
//backbuffer count is only constrained by video memory (we use discard swap-effect)
pp.backbuffer_count = 0;
for(int iwin = 0; iwin < 2; iwin++)
{
pp.windowed = windowed_vec[iwin];
for(int idb = 0; idb < 2; idb++)
{
pp.gdi_dialogbox_support = dialogbox_support_vec[idb];
//dialog box is always supported in windowd mode
if(windowed_vec[iwin] && !dialogbox_support_vec[idb])
continue;
for(byte4 imode = 0; imode < display_mode_vec.size(); imode++)
{
pp.display_mode = display_mode_vec[imode];
if(windowed_vec[iwin])
{
//in windowed mode, backbuffer size is arbitrary
pp.backbuffer_width = 0;
pp.backbuffer_height = 0;
}
else
{
//in fullscreen mode, backbuffer size should be the same as the display resolution
pp.backbuffer_width = display_mode_vec[imode].Width;
pp.backbuffer_height = display_mode_vec[imode].Height;
}
//windowed = true ==> display_mode = cur_display_mode
if(windowed_vec[iwin])
{
if( display_mode_vec[imode].Format != cur_display_mode.Format ||
display_mode_vec[imode].Width != cur_display_mode.Width ||
display_mode_vec[imode].Height != cur_display_mode.Height )
{
continue;
}
}
for(byte4 ibk = 0; ibk < bkbuf_fmt_count; ibk++)
{
pp.backbuffer_format = bkbuf_fmt_vec[ibk];
//in fullscreen mode, if dialog box is supported, bkbuf_fmt should be one of the {D3DFMT_X1R5G5B5, D3DFMT_R5G6B5, D3DFMT_X8R8G8B8}
if(!windowed_vec[iwin] && dialogbox_support_vec[idb])
{
if( bkbuf_fmt_vec[ibk] != D3DFMT_X1R5G5B5 &&
bkbuf_fmt_vec[ibk] != D3DFMT_R5G6B5 &&
bkbuf_fmt_vec[ibk] != D3DFMT_X8R8G8B8 )
{
continue;
}
}
//the backbuffer should be a render target
if(FAILED(d3d->CheckDeviceFormat(ordinal, D3DDEVTYPE_HAL, display_mode_vec[imode].Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, bkbuf_fmt_vec[ibk])))
continue;
//post pixel shader blending is required
if(FAILED(d3d->CheckDeviceFormat(ordinal, D3DDEVTYPE_HAL, display_mode_vec[imode].Format, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, bkbuf_fmt_vec[ibk])))
continue;
//(windowed, display_fmt, bkbuf_fmt) combo should pass the CheckDeviceType() check
if(FAILED(d3d->CheckDeviceType(ordinal, D3DDEVTYPE_HAL, display_mode_vec[imode].Format, bkbuf_fmt_vec[ibk], windowed_vec[iwin])))
continue;
for(byte4 ids = 0; ids < ds_fmt_count; ids++)
{
pp.depthstencil_format = ds_fmt_vec[ids];
//it should be a depth-stencil target
if(FAILED(d3d->CheckDeviceFormat(ordinal, D3DDEVTYPE_HAL, display_mode_vec[imode].Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, ds_fmt_vec[ids])))
continue;
//match with render-target
if(FAILED(d3d->CheckDepthStencilMatch(ordinal, D3DDEVTYPE_HAL, display_mode_vec[imode].Format, bkbuf_fmt_vec[ibk], ds_fmt_vec[ids])))
continue;
//D3DMULTISAMPLE_NONE is always valid, so we find one, add it
pp.multisample_type = D3DMULTISAMPLE_NONE;
pp.multisample_quality = 0;
valid_pp_vec.push_back(pp);
//in fullscreen mode, if dialog box is supported, multisample should be none
if(!windowed_vec[iwin] && dialogbox_support_vec[idb])
continue;
//if the multisample is not needed, continue
if(!multisample)
continue;
for(byte4 ms_type = 1; ms_type <= 16; ms_type++)
{
pp.multisample_type = (D3DMULTISAMPLE_TYPE)ms_type;
//the ms type should be supported by the render-target format
byte4 bk_quality_level_count;
if(FAILED(d3d->CheckDeviceMultiSampleType(ordinal, D3DDEVTYPE_HAL, bkbuf_fmt_vec[ibk], windowed_vec[iwin], (D3DMULTISAMPLE_TYPE)ms_type, &bk_quality_level_count)))
continue;
//the ms type should be supported by the depth-stencil format
byte4 ds_quality_level_count;
if(FAILED(d3d->CheckDeviceMultiSampleType(ordinal, D3DDEVTYPE_HAL, ds_fmt_vec[ids], windowed_vec[iwin], (D3DMULTISAMPLE_TYPE)ms_type, &ds_quality_level_count)))
continue;
//get max quality
byte4 max_quality_level_count = min(bk_quality_level_count, ds_quality_level_count);
for(byte4 quality = 0; quality < max_quality_level_count; quality++)
{
pp.multisample_quality = quality;
//find one, add it
valid_pp_vec.push_back(pp);
}
}
}
}
}
}
}
if(valid_pp_vec.empty())
return 0;
buffer_t buf;
D3DXCreateBuffer((byte4)valid_pp_vec.size() * sizeof(gpu_presentation_parameters_t), &buf);
memcpy(buf->GetBufferPointer(), &valid_pp_vec[0], buf->GetBufferSize());
*out = buf;
return (byte4)valid_pp_vec.size();
}
void _gpu_t::reflect_global_states()
{
//view / proj (shadowed -> board)
device->SetTransform(D3DTS_VIEW, &mat_view);
device->SetTransform(D3DTS_PROJECTION, &mat_proj);
if( viewproj_vsconst_reg != -1 )
{
device->SetVertexShaderConstantF(viewproj_vsconst_reg, (float*)&mat_viewproj_transpose, 4);
}
//viewport (board -> shadowed)
device->GetViewport(&cur_viewport);
matrix mat_viewport;
build_viewport_matrix(&mat_viewport, &cur_viewport);
mat_viewprojviewport = mat_viewproj * mat_viewport;
//global vs consts (shadowed -> board)
for( byte4 i = 0; i < global_vsconst_vec.size(); i++ )
{
device->SetVertexShaderConstantF(global_vsconst_vec[i].start_reg, (float*)&global_vsconst_vec[i].data[0], (byte4)global_vsconst_vec[i].data.size());
}
}
void _gpu_t::force_default_dynamic_states()
{
if( cur_cp.vertex_processing_method == _gpu_vertex_processing_method_mixed_ )
{
device->SetSoftwareVertexProcessing(false);
cur_drs.software_vertex_processing = false;
}
device->SetStreamSource(0, 0, 0, 0);
cur_drs.vertex_buffer = 0;
cur_drs.stride = 0;
device->SetIndices(0);
cur_drs.index_buffer = 0;
device->SetVertexDeclaration(0);
device->SetFVF(0);
cur_drs.use_fvf = true;
cur_drs.fvf = 0;
cur_drs.decl = 0;
device->SetVertexShader(0);
cur_drs.vertex_shader = 0;
device->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
cur_drs.vertex_blend = D3DVBF_DISABLE;
device->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, false);
cur_drs.indexed_vertex_blend_enable = false;
device->SetTexture(0, 0);
cur_drs.texture = 0;
device->SetRenderState(D3DRS_TEXTUREFACTOR, 0xffffffff);
cur_drs.texture_factor = 0xffffffff;
}
bool _gpu_t::turn_on(const gpu_creation_parameters_t* cp, const gpu_presentation_parameters_t* pp)
{
//create device
D3DDEVICE_CREATION_PARAMETERS d3dcp;
D3DPRESENT_PARAMETERS d3dpp;
translate_initialization_parameters(ordinal, cp, pp, &d3dcp, &d3dpp);
HRESULT hr = d3d->CreateDevice(d3dcp.AdapterOrdinal, d3dcp.DeviceType, d3dcp.hFocusWindow, d3dcp.BehaviorFlags, &d3dpp, &device);
if(FAILED(hr))
{
switch(hr)
{
case D3DERR_INVALIDCALL: _error_record_ = _gpu_error_invalid_params_; break;
case D3DERR_OUTOFVIDEOMEMORY: _error_record_ = _gpu_error_out_of_video_memory_; break;
case D3DERR_NOTAVAILABLE: _error_record_ = _gpu_error_hardware_acceleration_not_available_; break;
default: _error_record_ = _gpu_error_unknown_; break;
}
//fail creating device. didn't touch anything except the error record
return false;
}
//apply dialog box mode
if( !pp->windowed )
{
device->SetDialogBoxMode( pp->gdi_dialogbox_support );
}
//working caps
D3DCAPS9 d3dcaps;
device->GetDeviceCaps(&d3dcaps);
translate_caps(&d3dcaps, &working_caps);
//save creation/presentation parameters;
cur_cp = *cp;
cur_pp = *pp;
//global states
reflect_global_states();
//define default static states and set it as the current one
static_render_states_t default_srs;
default_srs.lighting = true;
default_srs.cull_mode = D3DCULL_CCW;
default_srs.alpha_test_enable = false;
default_srs.alpha_ref = 0;
default_srs.alpha_func = D3DCMP_ALWAYS;
default_srs.z_enable = D3DZB_TRUE;
default_srs.z_func = D3DCMP_LESSEQUAL;
default_srs.z_write_enable = true;
default_srs.alpha_blend_enable = false;
default_srs.blend_op = D3DBLENDOP_ADD;
default_srs.src_blend = D3DBLEND_ONE;
default_srs.dest_blend = D3DBLEND_ZERO;
default_srs.color_op = D3DTOP_MODULATE;
default_srs.color_arg1 = D3DTA_TEXTURE;
default_srs.color_arg2 = D3DTA_CURRENT;
default_srs.alpha_op = D3DTOP_SELECTARG1;
default_srs.alpha_arg1 = D3DTA_TEXTURE;
default_srs.alpha_arg2 = D3DTA_CURRENT;
default_srs.address_u = D3DTADDRESS_WRAP;
default_srs.address_v = D3DTADDRESS_WRAP;
default_srs.mag_filter = D3DTEXF_POINT;
default_srs.min_filter = D3DTEXF_POINT;
define_static_render_states(&default_srs); //the returned id will be 0
cur_srs = 0;
//dynamic states
force_default_dynamic_states();
return true;
}
void _gpu_t::turn_off()
{
//release dynamic vertex buffer
SAFE_RELEASE(dynvb);
dynvb_size = 0;
dynvb_used_bytes = 0;
dynvb_broken = false;
//clear static states
for( byte4 i = 0; i < transition_table.size(); i++ )
{
for( byte4 j = 0; j < transition_table[i].size(); j++ )
{
transition_table[i][j].clear();
}
}
srs_vec.clear();
transition_table.clear();
cur_srs = -1;
//release decls
map<byte4, d3ddecl_t>::iterator pdecl = decl_map.begin();
while( pdecl != decl_map.end() )
{
SAFE_RELEASE(pdecl->second);
pdecl++;
}
decl_map.clear();
//now all the device objects are destroyed, we can release the device
SAFE_RELEASE(device);
// if( device->Release() > 0 ) //for debugging & testing purpose
// MessageBox(0, "The D3D device has a non-zero reference count, meaning some objects were not released.", "Warning", MB_ICONERROR|MB_OK);
// device = 0;
//default working caps to hardware caps after turned off
D3DCAPS9 d3dcaps;
d3d->GetDeviceCaps(ordinal, D3DDEVTYPE_HAL, &d3dcaps);
translate_caps(&d3dcaps, &working_caps);
//creation/presentation is undefined now
memset(&cur_cp, 0, sizeof(cur_cp));
memset(&cur_pp, 0, sizeof(cur_pp));
//clear global states
D3DXMatrixIdentity(&mat_view);
D3DXMatrixIdentity(&mat_proj);
D3DXMatrixIdentity(&mat_viewproj);
D3DXMatrixIdentity(&mat_viewproj_transpose);
D3DXMatrixIdentity(&mat_viewprojviewport);
memset(&cur_viewport, 0, sizeof(cur_viewport));
viewproj_vsconst_reg = -1;
global_vsconst_vec.clear();
//clear dynamic states
memset(&cur_drs, 0, sizeof(cur_drs));
}
bool _gpu_t::reset(const gpu_presentation_parameters_t* pp)
{
//release dynamic vertex buffer (it will be recreated lazily after resetting successfully)
SAFE_RELEASE(dynvb);
dynvb_used_bytes = 0;
dynvb_broken = false;
//release stateblocks (they will be recreated lazily after resetting successfully)
for( byte4 i = 0; i < transition_table.size(); i++ )
{
for( byte4 j = 0; j < transition_table[i].size(); j++ )
{
transition_table[i][j].discard_video_objects();
}
}
//now all the video objects are discarded, we can reset the device (decls are not video objects)
D3DPRESENT_PARAMETERS d3dpp;
translate_initialization_parameters(ordinal, &cur_cp, pp, 0, &d3dpp);
HRESULT hr = device->Reset(&d3dpp);
if( FAILED(hr) )
{
switch(hr)
{
case D3DERR_INVALIDCALL: _error_record_ = _gpu_error_invalid_params_; break;
case D3DERR_OUTOFVIDEOMEMORY: _error_record_ = _gpu_error_out_of_video_memory_; break;
case D3DERR_DEVICELOST: _error_record_ = _gpu_error_device_lost_; break;
case D3DERR_DRIVERINTERNALERROR: _error_record_ = _gpu_error_driver_internal_error_; break;
default: _error_record_ = _gpu_error_unknown_; break;
}
return false;
}
//apply dialog box mode
if( !pp->windowed )
{
device->SetDialogBoxMode( pp->gdi_dialogbox_support );
}
//update cur_pp after successfully resetting
cur_pp = *pp;
//reflect global states
reflect_global_states();
//force to default static states
device->SetRenderState(D3DRS_LIGHTING, true);
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
device->SetRenderState(D3DRS_ALPHATESTENABLE, false);
device->SetRenderState(D3DRS_ALPHAREF, 0);
device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);
device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
device->SetRenderState(D3DRS_ZWRITEENABLE, true);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
cur_srs = 0;
//force to default dynamic states
force_default_dynamic_states();
return true;
}
bool _gpu_t::present()
{
HRESULT hr = device->Present(0, 0, 0, 0);
if( FAILED(hr) )
{
switch( hr )
{
case D3DERR_DEVICELOST: _error_record_ = _gpu_error_device_lost_; break;
case D3DERR_DRIVERINTERNALERROR: _error_record_ = _gpu_error_driver_internal_error_; break;
default: _error_record_ = _gpu_error_unknown_; break;
}
return false;
}
return true;
}
bool _gpu_t::test_cooperative_level()
{
HRESULT hr = device->TestCooperativeLevel();
if( FAILED(hr) )
{
switch( hr )
{
case D3DERR_DEVICELOST: _error_record_ = _gpu_error_device_lost_; break;
case D3DERR_DEVICENOTRESET: _error_record_ = _gpu_error_device_not_reset_; break;
case D3DERR_DRIVERINTERNALERROR: _error_record_ = _gpu_error_driver_internal_error_; break;
default: _error_record_ = _gpu_error_unknown_; break;
}
return false;
}
return true;
}
//dynamic caps
byte4 _gpu_t::build_valid_render_target_formats(buffer_t* out)
{
const D3DFORMAT fmt_vec[] =
{
D3DFMT_R8G8B8 ,
D3DFMT_A8R8G8B8 ,
D3DFMT_X8R8G8B8 ,
D3DFMT_R5G6B5 ,
D3DFMT_X1R5G5B5 ,
D3DFMT_A1R5G5B5 ,
D3DFMT_A4R4G4B4 ,
D3DFMT_R3G3B2 ,
D3DFMT_A8R3G3B2 ,
D3DFMT_X4R4G4B4 ,
D3DFMT_A2B10G10R10 ,
D3DFMT_A8B8G8R8 ,
D3DFMT_X8B8G8R8 ,
D3DFMT_A2R10G10B10 ,
D3DFMT_A16B16G16R16 ,
};
const byte4 fmt_count = sizeof(fmt_vec) / sizeof(fmt_vec[0]);
vector<D3DFORMAT> rt_fmt_vec;
rt_fmt_vec.reserve(16);
for(byte4 ifmt = 0; ifmt < fmt_count; ifmt++)
{
//it should be a render target
if(FAILED(d3d->CheckDeviceFormat(ordinal, D3DDEVTYPE_HAL, cur_pp.display_mode.Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, fmt_vec[ifmt])))
continue;
//compatible with ds
if(FAILED(d3d->CheckDepthStencilMatch(ordinal, D3DDEVTYPE_HAL, cur_pp.display_mode.Format, fmt_vec[ifmt], cur_pp.depthstencil_format)))
continue;
//multisample should match
byte4 quality_level_count;
if(FAILED(d3d->CheckDeviceMultiSampleType(ordinal, D3DDEVTYPE_HAL, fmt_vec[ifmt], cur_pp.windowed, cur_pp.multisample_type, &quality_level_count)))
continue;
if(quality_level_count < cur_pp.multisample_quality + 1)
continue;
//find one, add it
rt_fmt_vec.push_back(fmt_vec[ifmt]);
}
if(rt_fmt_vec.empty())
return 0;
buffer_t buf;
D3DXCreateBuffer((byte4)rt_fmt_vec.size() * sizeof(D3DFORMAT), &buf);
memcpy(buf->GetBufferPointer(), &rt_fmt_vec[0], buf->GetBufferSize());
*out = buf;
return (byte4)rt_fmt_vec.size();
}
byte4 _gpu_t::build_valid_render_target_texture_formats(buffer_t* out)
{
const D3DFORMAT fmt_vec[] =
{
D3DFMT_R8G8B8 ,
D3DFMT_A8R8G8B8 ,
D3DFMT_X8R8G8B8 ,
D3DFMT_R5G6B5 ,
D3DFMT_X1R5G5B5 ,
D3DFMT_A1R5G5B5 ,
D3DFMT_A4R4G4B4 ,
D3DFMT_R3G3B2 ,
D3DFMT_A8R3G3B2 ,
D3DFMT_X4R4G4B4 ,
D3DFMT_A2B10G10R10 ,
D3DFMT_A8B8G8R8 ,
D3DFMT_X8B8G8R8 ,
D3DFMT_A2R10G10B10 ,
D3DFMT_A16B16G16R16 ,
};
const byte4 fmt_count = sizeof(fmt_vec) / sizeof(fmt_vec[0]);
vector<D3DFORMAT> rt_tex_fmt_vec;
rt_tex_fmt_vec.reserve(16);
for(byte4 ifmt = 0; ifmt < fmt_count; ifmt++)
{
//it should be a render target
if(FAILED(d3d->CheckDeviceFormat(ordinal, D3DDEVTYPE_HAL, cur_pp.display_mode.Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, fmt_vec[ifmt])))
continue;
//compatible with ds
if(FAILED(d3d->CheckDepthStencilMatch(ordinal, D3DDEVTYPE_HAL, cur_pp.display_mode.Format, fmt_vec[ifmt], cur_pp.depthstencil_format)))
continue;
//find one, add it
rt_tex_fmt_vec.push_back(fmt_vec[ifmt]);
}
if( rt_tex_fmt_vec.empty() )
{
//ignore depth-stencil
for( byte4 ifmt = 0; ifmt < fmt_count; ifmt++ )
{
//it should be a render target
if( FAILED(d3d->CheckDeviceFormat(ordinal, D3DDEVTYPE_HAL, cur_pp.display_mode.Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, fmt_vec[ifmt])) )
continue;
//find one, add it
rt_tex_fmt_vec.push_back( fmt_vec[ifmt] );
}
}
if(rt_tex_fmt_vec.empty())
return 0;
buffer_t buf;
D3DXCreateBuffer((byte4)rt_tex_fmt_vec.size() * sizeof(D3DFORMAT), &buf);
memcpy(buf->GetBufferPointer(), &rt_tex_fmt_vec[0], buf->GetBufferSize());
*out = buf;
return (byte4)rt_tex_fmt_vec.size();
}
byte4 _gpu_t::build_valid_offscr_texture_formats(buffer_t* out)
{
const D3DFORMAT fmt_vec[] =
{
D3DFMT_R8G8B8 ,
D3DFMT_A8R8G8B8 ,
D3DFMT_X8R8G8B8 ,
D3DFMT_R5G6B5 ,
D3DFMT_X1R5G5B5 ,
D3DFMT_A1R5G5B5 ,
D3DFMT_A4R4G4B4 ,
D3DFMT_R3G3B2 ,
D3DFMT_A8 ,
D3DFMT_A8R3G3B2 ,
D3DFMT_X4R4G4B4 ,
D3DFMT_A2B10G10R10 ,
D3DFMT_A8B8G8R8 ,
D3DFMT_X8B8G8R8 ,
D3DFMT_A2R10G10B10 ,
D3DFMT_A16B16G16R16 ,
D3DFMT_DXT1 ,
D3DFMT_DXT2 ,
D3DFMT_DXT3 ,
D3DFMT_DXT4 ,
D3DFMT_DXT5 ,
};
const byte4 fmt_count = sizeof(fmt_vec) / sizeof(fmt_vec[0]);
vector<D3DFORMAT> offscr_tex_fmt_vec;
offscr_tex_fmt_vec.reserve(16);
for(byte4 ifmt = 0; ifmt < fmt_count; ifmt++)
{
//it should be a texture
if(FAILED(d3d->CheckDeviceFormat(ordinal, D3DDEVTYPE_HAL, cur_pp.display_mode.Format, 0, D3DRTYPE_TEXTURE, fmt_vec[ifmt])))
continue;
//find one, add it
offscr_tex_fmt_vec.push_back(fmt_vec[ifmt]);
}
if(offscr_tex_fmt_vec.empty())
return 0;
buffer_t buf;
D3DXCreateBuffer((byte4)offscr_tex_fmt_vec.size() * sizeof(D3DFORMAT), &buf);
memcpy(buf->GetBufferPointer(), &offscr_tex_fmt_vec[0], buf->GetBufferSize());
*out = buf;
return (byte4)offscr_tex_fmt_vec.size();
}
byte4 _gpu_t::build_valid_downsampling_target_formats(buffer_t* out)
{
//rt fmts
buffer_t rt_fmts_buf;
byte4 rt_fmt_count = build_valid_render_target_formats(&rt_fmts_buf);
if(!rt_fmt_count)
return 0;
D3DFORMAT* rt_fmt_vec = (D3DFORMAT*)rt_fmts_buf->GetBufferPointer();
//rt texture fmts
buffer_t rttex_fmts_buf;
byte4 rttex_fmt_count = build_valid_render_target_texture_formats(&rttex_fmts_buf);
if(!rttex_fmt_count)
{
rt_fmts_buf->Release();
return 0;
}
D3DFORMAT* rttex_fmt_vec = (D3DFORMAT*)rttex_fmts_buf->GetBufferPointer();
//loop & check
vector<D3DFORMAT> valid_dst_fmt_vec;
for(byte4 irttex = 0; irttex < rttex_fmt_count; irttex++)
{
for(byte4 irt = 0; irt < rt_fmt_count; irt++)
{
if(FAILED(d3d->CheckDeviceFormatConversion(ordinal, D3DDEVTYPE_HAL, rt_fmt_vec[irt], rttex_fmt_vec[irttex])))
continue;
//find one, add it
valid_dst_fmt_vec.push_back(rttex_fmt_vec[irttex]);
break;
}
}
rt_fmts_buf->Release();
rttex_fmts_buf->Release();
if(valid_dst_fmt_vec.empty())
return 0;
buffer_t buf;
D3DXCreateBuffer((byte4)valid_dst_fmt_vec.size() * sizeof(D3DFORMAT), &buf);
memcpy(buf->GetBufferPointer(), &valid_dst_fmt_vec[0], buf->GetBufferSize());
*out = buf;
return (byte4)valid_dst_fmt_vec.size();
}
byte4 _gpu_t::build_valid_downsampling_source_formats(D3DFORMAT target_format, buffer_t* out)
{
//rt fmts
buffer_t rt_fmts_buf;
byte4 rt_fmt_count = build_valid_render_target_formats(&rt_fmts_buf);
if(!rt_fmt_count)
return 0;
D3DFORMAT* rt_fmt_vec = (D3DFORMAT*)rt_fmts_buf->GetBufferPointer();
//loop & check
vector<D3DFORMAT> valid_src_fmt_vec;
for(byte4 irt = 0; irt < rt_fmt_count; irt++)
{
if(FAILED(d3d->CheckDeviceFormatConversion(ordinal, D3DDEVTYPE_HAL, rt_fmt_vec[irt], target_format)))
continue;
//find one, add it
valid_src_fmt_vec.push_back(rt_fmt_vec[irt]);
}
rt_fmts_buf->Release();
if(valid_src_fmt_vec.empty())
return 0;
buffer_t buf;
D3DXCreateBuffer((byte4)valid_src_fmt_vec.size() * sizeof(D3DFORMAT), &buf);
memcpy(buf->GetBufferPointer(), &valid_src_fmt_vec[0], buf->GetBufferSize());
*out = buf;
return (byte4)valid_src_fmt_vec.size();
}
//decl pool
bool _gpu_t::create_vertex_declaration(byte4 fvf, d3ddecl_t* out)
{
map<byte4, d3ddecl_t>::iterator pdecl = decl_map.find(fvf);
if(pdecl != decl_map.end())
{
pdecl->second->AddRef();
*out = pdecl->second;
return true;
}
else
{
D3DVERTEXELEMENT9 decl_elems[MAX_FVF_DECL_SIZE];
if(FAILED(D3DXDeclaratorFromFVF(fvf, decl_elems)))
return false;
d3ddecl_t decl;
if(FAILED(device->CreateVertexDeclaration(decl_elems, &decl)))
return false;
decl_map[fvf] = decl;
decl->AddRef();
*out = decl;
return true;
}
}
//dynamic vertex buffer
void _gpu_t::set_dynamic_vertex_buffer_size(byte4 size)
{
SAFE_RELEASE(dynvb);
dynvb_size = size;
dynvb_used_bytes = 0;
dynvb_broken = false;
}
void* _gpu_t::lock_dynamic_vertex_buffer(byte4 vertex_count, byte4 stride, byte4* start_vertex, d3dvb_t* vertex_buffer)
{
//create dynvb lazily
if( !dynvb && dynvb_size > 0 && !dynvb_broken )
{
if(FAILED(device->CreateVertexBuffer(dynvb_size, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &dynvb, 0)))
{
//creating dynamic vertex buffer failed, mark it as broken
dynvb_broken = true;
return 0;
}
}
//check dynvb
if(!dynvb)
return 0;
//align to a mutliple of 'stride'
dynvb_used_bytes += ((stride - dynvb_used_bytes % stride) % stride);
byte4 lock_size = stride * vertex_count;
if( dynvb_used_bytes + lock_size <= dynvb_size )
{
//lock with no-overwrite
void* v;
if(FAILED(dynvb->Lock(dynvb_used_bytes, lock_size, &v, D3DLOCK_NOOVERWRITE)))
return 0;
*start_vertex = dynvb_used_bytes / stride;
dynvb_used_bytes += lock_size;
*vertex_buffer = dynvb;
return v;
}
else
{
//fail if the buffer is not large enough
if(dynvb_size < lock_size)
return 0;
//lock with discard
void* v;
if(FAILED(dynvb->Lock(0, 0, &v, D3DLOCK_DISCARD)))
return 0;
*start_vertex = 0;
dynvb_used_bytes = lock_size;
*vertex_buffer = dynvb;
return v;
}
}
void _gpu_t::unlock_dynamic_vertex_buffer()
{
dynvb->Unlock();
}
//global render states
void _gpu_t::set_view_matrix(const matrix* view)
{
device->SetTransform(D3DTS_VIEW, view);
mat_view = *view;
mat_viewproj = mat_view * mat_proj;
D3DXMatrixTranspose(&mat_viewproj_transpose, &mat_viewproj);
if(viewproj_vsconst_reg != -1)
{
device->SetVertexShaderConstantF(viewproj_vsconst_reg, (float*)&mat_viewproj_transpose, 4);
}
matrix mat_viewport;
build_viewport_matrix(&mat_viewport, &cur_viewport);
mat_viewprojviewport = mat_viewproj * mat_viewport;
}
void _gpu_t::set_proj_matrix(const matrix* proj)
{
device->SetTransform(D3DTS_PROJECTION, proj);
mat_proj = *proj;
mat_viewproj = mat_view * mat_proj;
D3DXMatrixTranspose(&mat_viewproj_transpose, &mat_viewproj);
if(viewproj_vsconst_reg != -1)
{
device->SetVertexShaderConstantF(viewproj_vsconst_reg, (float*)&mat_viewproj_transpose, 4);
}
matrix mat_viewport;
build_viewport_matrix(&mat_viewport, &cur_viewport);
mat_viewprojviewport = mat_viewproj * mat_viewport;
}
void _gpu_t::set_viewport(const D3DVIEWPORT9* viewport)
{
device->SetViewport(viewport);
cur_viewport = *viewport;
matrix mat_viewport;
build_viewport_matrix(&mat_viewport, &cur_viewport);
mat_viewprojviewport = mat_viewproj * mat_viewport;
}
void _gpu_t::map_viewproj_to_vsconst(byte4 reg)
{
if(viewproj_vsconst_reg != -1)
{
//clear original
float4 v[4];
v[0] = v[1] = v[2] = v[3] = float4(0, 0, 0, 1);
device->SetVertexShaderConstantF(viewproj_vsconst_reg, (float*)&v[0], 4);
}
//map new one
viewproj_vsconst_reg = reg;
if(viewproj_vsconst_reg != -1)
{
device->SetVertexShaderConstantF(viewproj_vsconst_reg, (float*)&mat_viewproj_transpose, 4);
}
}
void _gpu_t::set_global_vsconst(byte4 reg, const float* data, byte4 float4_count)
{
byte4 old_size = (byte4)global_vsconst_vec.size();
global_vsconst_vec.resize(old_size + 1);
global_vsconst_vec[old_size].start_reg = reg;
global_vsconst_vec[old_size].data.resize(float4_count);
memcpy(&global_vsconst_vec[old_size].data[0], data, sizeof(float4) * float4_count);
device->SetVertexShaderConstantF(reg, data, float4_count);
}
//static render states
byte4 _gpu_t::define_static_render_states(const static_render_states_t* in)
{
byte4 old_size = (byte4)srs_vec.size();
srs_vec.push_back(*in);
transition_table.resize(old_size + 1);
for(byte4 i = 0; i < old_size + 1; i++)
{
transition_table[i].resize(old_size + 1);
}
for(byte4 i = 0; i < old_size + 1; i++)
{
transition_table[i][old_size].init(&srs_vec[i], &srs_vec[old_size]);
}
for(byte4 i = 0; i < old_size; i++)
{
transition_table[old_size][i].init(&srs_vec[old_size], &srs_vec[i]);
}
return old_size;
}
void _gpu_t::set_static_render_states(byte4 id)
{
transition_table[cur_srs][id].apply(device);
cur_srs = id;
}
//dynamic states
void _gpu_t::set_software_vertex_processing(bool software)
{
if(cur_cp.vertex_processing_method == _gpu_vertex_processing_method_mixed_ && cur_drs.software_vertex_processing != software)
{
device->SetSoftwareVertexProcessing(software);
cur_drs.software_vertex_processing = software;
}
}
void _gpu_t::set_stream_source(d3dvb_t vertex_buffer, byte4 stride)
{
if(cur_drs.vertex_buffer != vertex_buffer || cur_drs.stride != stride)
{
device->SetStreamSource(0, vertex_buffer, 0, stride);
cur_drs.vertex_buffer = vertex_buffer;
cur_drs.stride = stride;
}
}
void _gpu_t::set_indices(d3dib_t index_buffer)
{
if(cur_drs.index_buffer != index_buffer)
{
device->SetIndices(index_buffer);
cur_drs.index_buffer = index_buffer;
}
}
void _gpu_t::set_fvf(byte4 fvf)
{
if(!cur_drs.use_fvf || cur_drs.fvf != fvf)
{
device->SetFVF(fvf);
cur_drs.use_fvf = true;
cur_drs.fvf = fvf;
}
}
void _gpu_t::set_decl(d3ddecl_t decl)
{
if(cur_drs.use_fvf || cur_drs.decl != decl)
{
device->SetVertexDeclaration(decl);
cur_drs.use_fvf = false;
cur_drs.decl = decl;
}
}
void _gpu_t::set_vertex_shader(d3dvs_t vertex_shader)
{
if(cur_drs.vertex_shader != vertex_shader)
{
device->SetVertexShader(vertex_shader);
cur_drs.vertex_shader = vertex_shader;
}
}
void _gpu_t::set_vertex_shader_constants(byte4 start_register, const float* data, byte4 float4_count)
{
device->SetVertexShaderConstantF(start_register, data, float4_count);
}
void _gpu_t::set_vertex_blend(D3DVERTEXBLENDFLAGS vertex_blend)
{
if(cur_drs.vertex_blend != vertex_blend)
{
device->SetRenderState(D3DRS_VERTEXBLEND, vertex_blend);
cur_drs.vertex_blend = vertex_blend;
}
}
void _gpu_t::set_indexed_vertex_blend_enable(bool enable)
{
if(cur_drs.indexed_vertex_blend_enable != enable)
{
device->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, enable);
cur_drs.indexed_vertex_blend_enable = enable;
}
}
void _gpu_t::set_world_matrix(byte4 index, const matrix* world_matrix)
{
device->SetTransform(D3DTS_WORLDMATRIX(index), world_matrix);
}
void _gpu_t::set_texture(d3dtexture_t texture)
{
if(cur_drs.texture != texture)
{
device->SetTexture(0, texture);
cur_drs.texture = texture;
}
}
void _gpu_t::set_texture_factor(byte4 factor)
{
if(cur_drs.texture_factor != factor)
{
device->SetRenderState(D3DRS_TEXTUREFACTOR, factor);
cur_drs.texture_factor = factor;
}
}