panda3d/panda/src/dxgsg8/dxTextureContext8.cxx
2007-02-08 21:22:29 +00:00

1414 lines
45 KiB
C++

// Filename: dxTextureContext8.cxx
// Created by: georges (02Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "dxTextureContext8.h"
#include "config_dxgsg8.h"
#include "dxGraphicsStateGuardian8.h"
#include "pStatTimer.h"
#include "dxgsg8base.h"
#include <assert.h>
#include <time.h>
TypeHandle DXTextureContext8::_type_handle;
static const DWORD g_LowByteMask = 0x000000FF;
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DXTextureContext8::
DXTextureContext8(PreparedGraphicsObjects *pgo, Texture *tex) :
TextureContext(pgo, tex) {
if (dxgsg8_cat.is_spam()) {
dxgsg8_cat.spam()
<< "Creating DX texture [" << tex->get_name() << "], minfilter(" << tex->get_minfilter() << "), magfilter(" << tex->get_magfilter() << "), anisodeg(" << tex->get_anisotropic_degree() << ")\n";
}
_d3d_texture = NULL;
_d3d_2d_texture = NULL;
_d3d_volume_texture = NULL;
_d3d_cube_texture = NULL;
_has_mipmaps = false;
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
DXTextureContext8::
~DXTextureContext8() {
delete_texture();
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::create_texture
// Access: Public
// Description: Use panda texture's pixelbuffer to create a texture
// for the specified device. This code gets the
// attributes of the texture from the bitmap, creates
// the texture, and then copies the bitmap into the
// texture. The return value is true if the texture is
// successfully created, false otherwise.
////////////////////////////////////////////////////////////////////
bool DXTextureContext8::
create_texture(DXScreenData &scrn) {
HRESULT hr;
int num_alpha_bits; // number of alpha bits in texture pixfmt
D3DFORMAT target_pixel_format = D3DFMT_UNKNOWN;
bool needs_luminance = false;
bool compress_texture = false;
nassertr(IS_VALID_PTR(get_texture()), false);
delete_texture();
#ifdef DO_PSTATS
update_data_size_bytes(get_texture()->estimate_texture_memory());
#endif // DO_PSTATS
// bpp indicates requested fmt, not texture fmt
DWORD target_bpp = get_bits_per_pixel(get_texture()->get_format(), &num_alpha_bits);
DWORD num_color_channels = get_texture()->get_num_components();
//PRINT_REFCNT(dxgsg8, scrn._d3d8);
DWORD orig_width = (DWORD)get_texture()->get_x_size();
DWORD orig_height = (DWORD)get_texture()->get_y_size();
DWORD orig_depth = (DWORD)get_texture()->get_z_size();
if ((get_texture()->get_format() == Texture::F_luminance_alpha)||
(get_texture()->get_format() == Texture::F_luminance_alphamask) ||
(get_texture()->get_format() == Texture::F_luminance)) {
needs_luminance = true;
}
if (num_alpha_bits > 0) {
if (num_color_channels == 3) {
dxgsg8_cat.error()
<< "texture " << get_texture()->get_name()
<< " has no inherent alpha channel, but alpha format is requested!\n";
}
}
_d3d_format = D3DFMT_UNKNOWN;
// figure out what 'D3DFMT' the Texture is in, so D3DXLoadSurfFromMem knows how to perform copy
switch (num_color_channels) {
case 1:
if (num_alpha_bits > 0) {
_d3d_format = D3DFMT_A8;
} else if (needs_luminance) {
_d3d_format = D3DFMT_L8;
}
break;
case 2:
nassertr(needs_luminance && (num_alpha_bits > 0), false);
_d3d_format = D3DFMT_A8L8;
break;
case 3:
_d3d_format = D3DFMT_R8G8B8;
break;
case 4:
_d3d_format = D3DFMT_A8R8G8B8;
break;
}
// check for texture compression
switch (get_texture()->get_texture_type()) {
case Texture::TT_1d_texture:
case Texture::TT_2d_texture:
case Texture::TT_cube_map:
// check config setting
if (compressed_textures) {
// no compression for render target textures
if (get_texture()->get_render_to_texture() == false) {
compress_texture = true;
}
}
break;
case Texture::TT_3d_texture:
// not supported by all video chips
break;
}
// make sure we handled all the possible cases
nassertr(_d3d_format != D3DFMT_UNKNOWN, false);
DWORD target_width = orig_width;
DWORD target_height = orig_height;
DWORD target_depth = orig_depth;
DWORD filter_caps;
switch (get_texture()->get_texture_type()) {
case Texture::TT_1d_texture:
case Texture::TT_2d_texture:
filter_caps = scrn._d3dcaps.TextureFilterCaps;
if (target_width > scrn._d3dcaps.MaxTextureWidth) {
target_width = scrn._d3dcaps.MaxTextureWidth;
}
if (target_height > scrn._d3dcaps.MaxTextureHeight) {
target_height = scrn._d3dcaps.MaxTextureHeight;
}
if (scrn._d3dcaps.TextureCaps & D3DPTEXTURECAPS_POW2) {
if (!ISPOW2(target_width)) {
target_width = down_to_power_2(target_width);
}
if (!ISPOW2(target_height)) {
target_height = down_to_power_2(target_height);
}
}
break;
case Texture::TT_3d_texture:
if ((scrn._d3dcaps.TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP) == 0) {
dxgsg8_cat.warning()
<< "3-d textures are not supported by this graphics driver.\n";
return false;
}
filter_caps = scrn._d3dcaps.VolumeTextureFilterCaps;
if (target_width > scrn._d3dcaps.MaxVolumeExtent) {
target_width = scrn._d3dcaps.MaxVolumeExtent;
}
if (target_height > scrn._d3dcaps.MaxVolumeExtent) {
target_height = scrn._d3dcaps.MaxVolumeExtent;
}
if (target_depth > scrn._d3dcaps.MaxVolumeExtent) {
target_depth = scrn._d3dcaps.MaxVolumeExtent;
}
if (scrn._d3dcaps.TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP_POW2) {
if (!ISPOW2(target_width)) {
target_width = down_to_power_2(target_width);
}
if (!ISPOW2(target_height)) {
target_height = down_to_power_2(target_height);
}
if (!ISPOW2(target_depth)) {
target_depth = down_to_power_2(target_depth);
}
}
break;
case Texture::TT_cube_map:
if ((scrn._d3dcaps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP) == 0) {
dxgsg8_cat.warning()
<< "Cube map textures are not supported by this graphics driver.\n";
return false;
}
filter_caps = scrn._d3dcaps.CubeTextureFilterCaps;
if (target_width > scrn._d3dcaps.MaxTextureWidth) {
target_width = scrn._d3dcaps.MaxTextureWidth;
}
if (scrn._d3dcaps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP_POW2) {
if (!ISPOW2(target_width)) {
target_width = down_to_power_2(target_width);
}
}
target_height = target_width;
break;
}
// checks for SQUARE reqmt (nvidia riva128 needs this)
if ((target_width != target_height) &&
(scrn._d3dcaps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) != 0) {
// assume pow2 textures. sum exponents, divide by 2 rounding down
// to get sq size
int i, width_exp, height_exp;
for (i = target_width, width_exp = 0; i > 1; width_exp++, i >>= 1) {
}
for (i = target_height, height_exp = 0; i > 1; height_exp++, i >>= 1) {
}
target_height = target_width = 1<<((width_exp+height_exp)>>1);
}
bool shrink_original = false;
if (orig_width != target_width || orig_height != target_height ||
orig_depth != target_depth) {
if (get_texture()->get_texture_type() == Texture::TT_3d_texture) {
dxgsg8_cat.info()
<< "Reducing size of " << get_texture()->get_name()
<< " from " << orig_width << "x" << orig_height << "x" << orig_depth
<< " to " << target_width << "x" << target_height
<< "x" << target_depth << "\n";
} else {
dxgsg8_cat.info()
<< "Reducing size of " << get_texture()->get_name()
<< " from " << orig_width << "x" << orig_height
<< " to " << target_width << "x" << target_height << "\n";
}
shrink_original = true;
}
const char *error_message;
error_message = "create_texture failed: couldn't find compatible device Texture Pixel Format for input texture";
if (dxgsg8_cat.is_spam()) {
dxgsg8_cat.spam()
<< "create_texture handling target bitdepth: " << target_bpp
<< " alphabits: " << num_alpha_bits << endl;
}
// I could possibly replace some of this logic with
// D3DXCheckTextureRequirements(), but it wouldn't handle all my
// specialized low-memory cases perfectly
#define CONVTYPE_STMT
#define CHECK_FOR_FMT(FMT, CONV) \
if (scrn._supported_tex_formats_mask & FMT##_FLAG) { \
CONVTYPE_STMT; \
target_pixel_format = D3DFMT_##FMT; \
goto found_matching_format; }
// handle each target bitdepth separately. might be less confusing
// to reorg by num_color_channels (input type, rather than desired
// 1st target)
switch (target_bpp) {
// IMPORTANT NOTE:
// target_bpp is REQUESTED bpp, not what exists in the texture
// array (the texture array contains num_color_channels*8bits)
case 32:
if (!((num_color_channels == 3) || (num_color_channels == 4)))
break; //bail
if (!dx_force_16bpptextures) {
if (compress_texture) {
CHECK_FOR_FMT(DXT3, Conv32toDXT3);
}
if (num_color_channels == 4) {
CHECK_FOR_FMT(A8R8G8B8, Conv32to32);
} else {
CHECK_FOR_FMT(A8R8G8B8, Conv24to32);
}
}
if (num_alpha_bits>0) {
nassertr(num_color_channels == 4, false);
// no 32-bit fmt, look for 16 bit w/alpha (1-15)
// 32 bit RGBA was requested, but only 16 bit alpha fmts are
// avail. By default, convert to 4-4-4-4 which has 4-bit alpha
// for blurry edges. If we know tex only needs 1 bit alpha
// (i.e. for a mask), use 1555 instead.
// ConversionType ConvTo1 = Conv32to16_4444, ConvTo2 = Conv32to16_1555;
// DWORD dwAlphaMask1 = 0xF000, dwAlphaMask2 = 0x8000;
// assume ALPHAMASK is x8000 and RGBMASK is x7fff to simplify
// 32->16 conversion. This should be true on most cards.
#ifndef FORCE_16bpp_1555
if (num_alpha_bits == 1)
#endif
{
CHECK_FOR_FMT(A1R5G5B5, Conv32to16_1555);
}
// normally prefer 4444 due to better alpha channel resolution
CHECK_FOR_FMT(A4R4G4B4, Conv32to16_4444);
CHECK_FOR_FMT(A1R5G5B5, Conv32to16_1555);
// At this point, bail. Don't worry about converting to
// non-alpha formats yet, I think this will be a very rare case.
error_message = "create_texture failed: couldn't find compatible Tex DDPIXELFORMAT! no available 16 or 32-bit alpha formats!";
} else {
// convert 3 or 4 channel to closest 16bpp color fmt
if (num_color_channels == 3) {
CHECK_FOR_FMT(R5G6B5, Conv24to16_4444);
CHECK_FOR_FMT(X1R5G5B5, Conv24to16_X555);
} else {
CHECK_FOR_FMT(R5G6B5, Conv32to16_4444);
CHECK_FOR_FMT(X1R5G5B5, Conv32to16_X555);
}
}
break;
case 24:
nassertr(num_color_channels == 3, false);
if (compress_texture) {
CHECK_FOR_FMT(DXT1, Conv24toDXT1);
}
if (!dx_force_16bpptextures) {
CHECK_FOR_FMT(R8G8B8, Conv24to24);
// no 24-bit fmt. look for 32 bit fmt (note: this is
// memory-hogging choice instead I could look for
// memory-conserving 16-bit fmt).
CHECK_FOR_FMT(X8R8G8B8, Conv24to32);
}
// no 24-bit or 32 fmt. look for 16 bit fmt (higher res 565 1st)
CHECK_FOR_FMT(R5G6B5, Conv24to16_0565);
CHECK_FOR_FMT(X1R5G5B5, Conv24to16_X555);
break;
case 16:
if (needs_luminance) {
nassertr(num_alpha_bits > 0, false);
nassertr(num_color_channels == 2, false);
CHECK_FOR_FMT(A8L8, ConvLum16to16);
if (!dx_force_16bpptextures) {
CHECK_FOR_FMT(A8R8G8B8, ConvLum16to32);
}
#ifndef FORCE_16bpp_1555
if (num_alpha_bits == 1)
#endif
{
CHECK_FOR_FMT(A1R5G5B5, ConvLum16to16_1555);
}
// normally prefer 4444 due to better alpha channel resolution
CHECK_FOR_FMT(A4R4G4B4, ConvLum16to16_4444);
CHECK_FOR_FMT(A1R5G5B5, ConvLum16to16_1555);
} else {
nassertr((num_color_channels == 3)||(num_color_channels == 4), false);
// look for compatible 16bit fmts, if none then give up
// (dont worry about other bitdepths for 16 bit)
switch(num_alpha_bits) {
case 0:
if (num_color_channels == 3) {
CHECK_FOR_FMT(R5G6B5, Conv24to16_0565);
CHECK_FOR_FMT(X1R5G5B5, Conv24to16_X555);
} else {
nassertr(num_color_channels == 4, false);
// it could be 4 if user asks us to throw away the alpha channel
CHECK_FOR_FMT(R5G6B5, Conv32to16_0565);
CHECK_FOR_FMT(X1R5G5B5, Conv32to16_X555);
}
break;
case 1:
// app specifically requests 1-5-5-5 F_rgba5 case, where you
// explicitly want 1-5-5-5 fmt, as opposed to F_rgbm, which
// could use 32bpp ARGB. fail if this particular fmt not
// avail.
nassertr(num_color_channels == 4, false);
CHECK_FOR_FMT(X1R5G5B5, Conv32to16_X555);
break;
case 4:
// app specifically requests 4-4-4-4 F_rgba4 case, as opposed
// to F_rgba, which could use 32bpp ARGB
nassertr(num_color_channels == 4, false);
CHECK_FOR_FMT(A4R4G4B4, Conv32to16_4444);
break;
default:
nassertr(false, false); // problem in get_bits_per_pixel()?
}
}
case 8:
if (needs_luminance) {
// dont bother handling those other 8bit lum fmts like 4-4,
// since 16 8-8 is usually supported too
nassertr(num_color_channels == 1, false);
// look for native lum fmt first
CHECK_FOR_FMT(L8, ConvLum8to8);
CHECK_FOR_FMT(L8, ConvLum8to16_A8L8);
if (!dx_force_16bpptextures) {
CHECK_FOR_FMT(R8G8B8, ConvLum8to24);
CHECK_FOR_FMT(X8R8G8B8, ConvLum8to32);
}
CHECK_FOR_FMT(R5G6B5, ConvLum8to16_0565);
CHECK_FOR_FMT(X1R5G5B5, ConvLum8to16_X555);
} else if (num_alpha_bits == 8) {
// look for 16bpp A8L8, else 32-bit ARGB, else 16-4444.
// skip 8bit alpha only (D3DFMT_A8), because I think only voodoo
// supports it and the voodoo support isn't the kind of blending
// model we need somehow (is it that voodoo assumes color is
// white? isnt that what we do in ConvAlpha8to32 anyway?)
CHECK_FOR_FMT(A8L8, ConvAlpha8to16_A8L8);
if (!dx_force_16bpptextures) {
CHECK_FOR_FMT(A8R8G8B8, ConvAlpha8to32);
}
CHECK_FOR_FMT(A4R4G4B4, ConvAlpha8to16_4444);
}
break;
default:
error_message = "create_texture failed: unhandled pixel bitdepth in DX loader";
}
// if we've gotten here, haven't found a match
dxgsg8_cat.error()
<< error_message << ": " << get_texture()->get_name() << endl
<< "NumColorChannels: " << num_color_channels << "; NumAlphaBits: "
<< num_alpha_bits << "; targetbpp: " <<target_bpp
<< "; _supported_tex_formats_mask: 0x"
<< (void*)scrn._supported_tex_formats_mask
<< "; NeedLuminance: " << needs_luminance << endl;
goto error_exit;
///////////////////////////////////////////////////////////
found_matching_format:
// We found a suitable format that matches the texture's format.
if (get_texture()->get_match_framebuffer_format()) {
// Instead of creating a texture with the found format, we will
// need to make one that exactly matches the framebuffer's
// format. Look up what that format is.
IDirect3DSurface8 *render_target;
hr = scrn._d3d_device->GetRenderTarget(&render_target);
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "GetRenderTgt failed in create_texture: " << D3DERRORSTRING(hr);
} else {
D3DSURFACE_DESC surface_desc;
hr = render_target->GetDesc(&surface_desc);
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "GetDesc failed in create_texture: " << D3DERRORSTRING(hr);
} else {
if (target_pixel_format != surface_desc.Format) {
if (dxgsg8_cat.is_debug()) {
dxgsg8_cat.debug()
<< "Chose format " << D3DFormatStr(surface_desc.Format)
<< " instead of " << D3DFormatStr(target_pixel_format)
<< " for texture to match framebuffer.\n";
}
target_pixel_format = surface_desc.Format;
}
}
SAFE_RELEASE(render_target);
}
}
// validate magfilter setting
// degrade filtering if no HW support
Texture::FilterType ft;
ft = get_texture()->get_magfilter();
if ((ft != Texture::FT_linear) && ft != Texture::FT_nearest) {
// mipmap settings make no sense for magfilter
if (ft == Texture::FT_nearest_mipmap_nearest) {
ft = Texture::FT_nearest;
} else {
ft = Texture::FT_linear;
}
}
if (ft == Texture::FT_linear &&
(filter_caps & D3DPTFILTERCAPS_MAGFLINEAR) == 0) {
ft = Texture::FT_nearest;
}
get_texture()->set_magfilter(ft);
// figure out if we are mipmapping this texture
ft = get_texture()->get_minfilter();
_has_mipmaps = false;
if (!dx_ignore_mipmaps) { // set if no HW mipmap capable
switch(ft) {
case Texture::FT_nearest_mipmap_nearest:
case Texture::FT_linear_mipmap_nearest:
case Texture::FT_nearest_mipmap_linear: // pick nearest in each, interpolate linearly b/w them
case Texture::FT_linear_mipmap_linear:
_has_mipmaps = true;
}
if (dx_mipmap_everything) { // debug toggle, ok to leave in since its just a creation cost
_has_mipmaps = true;
if (dxgsg8_cat.is_spam()) {
if (ft != Texture::FT_linear_mipmap_linear) {
dxgsg8_cat.spam()
<< "Forcing trilinear mipmapping on DX texture ["
<< get_texture()->get_name() << "]\n";
}
}
ft = Texture::FT_linear_mipmap_linear;
get_texture()->set_minfilter(ft);
}
} else if ((ft == Texture::FT_nearest_mipmap_nearest) || // cvt to no-mipmap filter types
(ft == Texture::FT_nearest_mipmap_linear)) {
ft = Texture::FT_nearest;
} else if ((ft == Texture::FT_linear_mipmap_nearest) ||
(ft == Texture::FT_linear_mipmap_linear)) {
ft = Texture::FT_linear;
}
nassertr((filter_caps & D3DPTFILTERCAPS_MINFPOINT) != 0, false);
#define TRILINEAR_MIPMAP_TEXFILTERCAPS (D3DPTFILTERCAPS_MIPFLINEAR | D3DPTFILTERCAPS_MINFLINEAR)
// do any other filter type degradations necessary
switch(ft) {
case Texture::FT_linear_mipmap_linear:
if ((filter_caps & TRILINEAR_MIPMAP_TEXFILTERCAPS) != TRILINEAR_MIPMAP_TEXFILTERCAPS) {
if (filter_caps & D3DPTFILTERCAPS_MINFLINEAR) {
ft = Texture::FT_linear_mipmap_nearest;
} else {
// if you cant do linear in a level, you probably cant do
// linear b/w levels, so just do nearest-all
ft = Texture::FT_nearest_mipmap_nearest;
}
}
break;
case Texture::FT_nearest_mipmap_linear:
// if we dont have bilinear, do nearest_nearest
if (!((filter_caps & D3DPTFILTERCAPS_MIPFPOINT) &&
(filter_caps & D3DPTFILTERCAPS_MINFLINEAR))) {
ft = Texture::FT_nearest_mipmap_nearest;
}
break;
case Texture::FT_linear_mipmap_nearest:
// if we dont have mip linear, do nearest_nearest
if (!(filter_caps & D3DPTFILTERCAPS_MIPFLINEAR)) {
ft = Texture::FT_nearest_mipmap_nearest;
}
break;
case Texture::FT_linear:
if (!(filter_caps & D3DPTFILTERCAPS_MINFLINEAR)) {
ft = Texture::FT_nearest;
}
break;
}
get_texture()->set_minfilter(ft);
uint aniso_degree;
aniso_degree = 1;
if (scrn._d3dcaps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) {
aniso_degree = get_texture()->get_anisotropic_degree();
if ((aniso_degree>scrn._d3dcaps.MaxAnisotropy) ||
dx_force_anisotropic_filtering) {
aniso_degree = scrn._d3dcaps.MaxAnisotropy;
}
}
get_texture()->set_anisotropic_degree(aniso_degree);
#ifdef _DEBUG
dxgsg8_cat.spam()
<< "create_texture: setting aniso degree for " << get_texture()->get_name()
<< " to: " << aniso_degree << endl;
#endif
UINT mip_level_count;
if (_has_mipmaps) {
// tell CreateTex to alloc space for all mip levels down to 1x1
mip_level_count = 0;
if (dxgsg8_cat.is_debug()) {
dxgsg8_cat.debug()
<< "create_texture: generating mipmaps for " << get_texture()->get_name()
<< endl;
}
} else {
mip_level_count = 1;
}
DWORD usage;
D3DPOOL pool;
usage = 0;
if (get_texture()->get_render_to_texture ()) {
// REQUIRED
pool = D3DPOOL_DEFAULT;
if (support_render_texture && scrn._dxgsg8 -> get_supports_render_texture ( )) {
target_pixel_format = scrn._render_to_texture_d3d_format;
usage |= D3DUSAGE_RENDERTARGET;
}
else {
target_pixel_format = scrn._framebuffer_d3d_format;
}
}
else {
pool = D3DPOOL_MANAGED;
}
switch (get_texture()->get_texture_type()) {
case Texture::TT_1d_texture:
case Texture::TT_2d_texture:
hr = scrn._d3d_device->CreateTexture
(target_width, target_height, mip_level_count, usage,
target_pixel_format, pool, &_d3d_2d_texture);
_d3d_texture = _d3d_2d_texture;
break;
case Texture::TT_3d_texture:
hr = scrn._d3d_device->CreateVolumeTexture
(target_width, target_height, target_depth, mip_level_count, usage,
target_pixel_format, pool, &_d3d_volume_texture);
_d3d_texture = _d3d_volume_texture;
break;
case Texture::TT_cube_map:
hr = scrn._d3d_device->CreateCubeTexture
(target_width, mip_level_count, usage,
target_pixel_format, pool, &_d3d_cube_texture);
_d3d_texture = _d3d_cube_texture;
break;
}
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "D3D create_texture failed!" << D3DERRORSTRING(hr);
goto error_exit;
}
if (dxgsg8_cat.is_debug()) {
dxgsg8_cat.debug()
<< "create_texture: " << get_texture()->get_name()
<< " converting panda equivalent of " << D3DFormatStr(_d3d_format)
<< " => " << D3DFormatStr(target_pixel_format) << endl;
}
hr = fill_d3d_texture_pixels();
if (FAILED(hr)) {
goto error_exit;
}
get_texture()->texture_uploaded();
mark_loaded();
return true;
error_exit:
RELEASE(_d3d_texture, dxgsg8, "texture", RELEASE_ONCE);
_d3d_2d_texture = NULL;
_d3d_volume_texture = NULL;
_d3d_cube_texture = NULL;
return false;
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::delete_texture
// Access: Public
// Description: Release the surface used to store the texture
////////////////////////////////////////////////////////////////////
void DXTextureContext8::
delete_texture() {
if (_d3d_texture == NULL) {
// dont bother printing the msg below, since we already released it.
return;
}
RELEASE(_d3d_texture, dxgsg8, "texture", RELEASE_ONCE);
_d3d_2d_texture = NULL;
_d3d_volume_texture = NULL;
_d3d_cube_texture = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::d3d_surface_to_texture
// Access: Public, Static
// Description: copies source_rect in pD3DSurf to upper left of
// texture
////////////////////////////////////////////////////////////////////
HRESULT DXTextureContext8::
d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface8 *d3d_surface,
bool inverted, Texture *result, int z) {
// still need custom conversion since d3d/d3dx has no way to convert
// arbitrary fmt to ARGB in-memory user buffer
HRESULT hr;
DWORD num_components = result->get_num_components();
nassertr(result->get_component_width() == sizeof(BYTE), E_FAIL); // cant handle anything else now
nassertr(result->get_component_type() == Texture::T_unsigned_byte, E_FAIL); // cant handle anything else now
nassertr((num_components == 3) || (num_components == 4), E_FAIL); // cant handle anything else now
nassertr(IS_VALID_PTR(d3d_surface), E_FAIL);
BYTE *buf = result->modify_ram_image();
if (z >= 0) {
nassertr(z < result->get_z_size(), E_FAIL);
buf += z * result->get_expected_ram_page_size();
}
if (IsBadWritePtr(d3d_surface, sizeof(DWORD))) {
dxgsg8_cat.error()
<< "d3d_surface_to_texture failed: bad pD3DSurf ptr value ("
<< ((void*)d3d_surface) << ")\n";
exit(1);
}
DWORD x_window_offset, y_window_offset;
DWORD copy_width, copy_height;
D3DLOCKED_RECT locked_rect;
D3DSURFACE_DESC surface_desc;
hr = d3d_surface->GetDesc(&surface_desc);
x_window_offset = source_rect.left, y_window_offset = source_rect.top;
copy_width = RECT_XSIZE(source_rect);
copy_height = RECT_YSIZE(source_rect);
// make sure there's enough space in the texture, its size must
// match (especially xsize) or scanlines will be too long
if (!((copy_width == result->get_x_size()) && (copy_height <= (DWORD)result->get_y_size()))) {
dxgsg8_cat.error()
<< "d3d_surface_to_texture, Texture size (" << result->get_x_size()
<< ", " << result->get_y_size()
<< ") too small to hold display surface ("
<< copy_width << ", " << copy_height << ")\n";
nassertr(false, E_FAIL);
return E_FAIL;
}
hr = d3d_surface->LockRect(&locked_rect, (CONST RECT*)NULL, (D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE));
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "d3d_surface_to_texture LockRect() failed!" << D3DERRORSTRING(hr);
return hr;
}
// ones not listed not handled yet
nassertr((surface_desc.Format == D3DFMT_A8R8G8B8) ||
(surface_desc.Format == D3DFMT_X8R8G8B8) ||
(surface_desc.Format == D3DFMT_R8G8B8) ||
(surface_desc.Format == D3DFMT_R5G6B5) ||
(surface_desc.Format == D3DFMT_X1R5G5B5) ||
(surface_desc.Format == D3DFMT_A1R5G5B5) ||
(surface_desc.Format == D3DFMT_A4R4G4B4), E_FAIL);
//buf contains raw ARGB in Texture byteorder
int byte_pitch = locked_rect.Pitch;
BYTE *surface_bytes = (BYTE *)locked_rect.pBits;
if (inverted) {
surface_bytes += byte_pitch * (y_window_offset + copy_height - 1);
byte_pitch = -byte_pitch;
} else {
surface_bytes += byte_pitch * y_window_offset;
}
// writes out last line in DDSurf first in PixelBuf, so Y line order
// precedes inversely
if (dxgsg8_cat.is_debug()) {
dxgsg8_cat.debug()
<< "d3d_surface_to_texture converting "
<< D3DFormatStr(surface_desc.Format)
<< " DDSurf to " << num_components << "-channel panda Texture\n";
}
DWORD *dest_word = (DWORD *)buf;
BYTE *dest_byte = (BYTE *)buf;
switch(surface_desc.Format) {
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8: {
if (num_components == 4) {
DWORD *source_word;
BYTE *dest_line = (BYTE*)dest_word;
for (DWORD y = 0; y < copy_height; y++) {
source_word = ((DWORD*)surface_bytes) + x_window_offset;
memcpy(dest_line, source_word, byte_pitch);
dest_line += byte_pitch;
surface_bytes += byte_pitch;
}
} else {
// 24bpp texture case (numComponents == 3)
DWORD *source_word;
for (DWORD y = 0; y < copy_height; y++) {
source_word = ((DWORD*)surface_bytes) + x_window_offset;
for (DWORD x = 0; x < copy_width; x++) {
BYTE r, g, b;
DWORD pixel = *source_word;
r = (BYTE)((pixel>>16) & g_LowByteMask);
g = (BYTE)((pixel>> 8) & g_LowByteMask);
b = (BYTE)((pixel ) & g_LowByteMask);
*dest_byte++ = b;
*dest_byte++ = g;
*dest_byte++ = r;
source_word++;
}
surface_bytes += byte_pitch;
}
}
break;
}
case D3DFMT_R8G8B8: {
BYTE *source_byte;
if (num_components == 4) {
for (DWORD y = 0; y < copy_height; y++) {
source_byte = surface_bytes + x_window_offset * 3 * sizeof(BYTE);
for (DWORD x = 0; x < copy_width; x++) {
DWORD r, g, b;
b = *source_byte++;
g = *source_byte++;
r = *source_byte++;
*dest_word = 0xFF000000 | (r << 16) | (g << 8) | b;
dest_word++;
}
surface_bytes += byte_pitch;
}
} else {
// 24bpp texture case (numComponents == 3)
for (DWORD y = 0; y < copy_height; y++) {
source_byte = surface_bytes + x_window_offset * 3 * sizeof(BYTE);
memcpy(dest_byte, source_byte, byte_pitch);
dest_byte += byte_pitch;
surface_bytes += byte_pitch;
}
}
break;
}
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5:
case D3DFMT_A1R5G5B5:
case D3DFMT_A4R4G4B4: {
WORD *source_word;
// handle 0555, 1555, 0565, 4444 in same loop
BYTE redshift, greenshift, blueshift;
DWORD redmask, greenmask, bluemask;
if (surface_desc.Format == D3DFMT_R5G6B5) {
redshift = (11-3);
redmask = 0xF800;
greenmask = 0x07E0;
greenshift = (5-2);
bluemask = 0x001F;
blueshift = 3;
} else if (surface_desc.Format == D3DFMT_A4R4G4B4) {
redmask = 0x0F00;
redshift = 4;
greenmask = 0x00F0;
greenshift = 0;
bluemask = 0x000F;
blueshift = 4;
} else { // 1555 or x555
redmask = 0x7C00;
redshift = (10-3);
greenmask = 0x03E0;
greenshift = (5-3);
bluemask = 0x001F;
blueshift = 3;
}
if (num_components == 4) {
// Note: these 16bpp loops ignore input alpha completely (alpha
// is set to fully opaque in texture!)
// if we need to capture alpha, probably need to make separate
// loops for diff 16bpp fmts for best speed
for (DWORD y = 0; y < copy_height; y++) {
source_word = ((WORD*)surface_bytes) + x_window_offset;
for (DWORD x = 0; x < copy_width; x++) {
WORD pixel = *source_word;
BYTE r, g, b;
b = (pixel & bluemask) << blueshift;
g = (pixel & greenmask) >> greenshift;
r = (pixel & redmask) >> redshift;
// alpha is just set to 0xFF
*dest_word = 0xFF000000 | (r << 16) | (g << 8) | b;
source_word++;
dest_word++;
}
surface_bytes += byte_pitch;
}
} else {
// 24bpp texture case (numComponents == 3)
for (DWORD y = 0; y < copy_height; y++) {
source_word = ((WORD*)surface_bytes) + x_window_offset;
for (DWORD x = 0; x < copy_width; x++) {
WORD pixel = *source_word;
BYTE r, g, b;
b = (pixel & bluemask) << blueshift;
g = (pixel & greenmask) >> greenshift;
r = (pixel & redmask) >> redshift;
*dest_byte++ = b;
*dest_byte++ = g;
*dest_byte++ = r;
source_word++;
}
surface_bytes += byte_pitch;
}
}
break;
}
default:
dxgsg8_cat.error()
<< "d3d_surface_to_texture: unsupported D3DFORMAT!\n";
}
d3d_surface->UnlockRect();
return S_OK;
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::fill_d3d_texture_pixels
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
HRESULT DXTextureContext8::
fill_d3d_texture_pixels() {
if (get_texture()->get_texture_type() == Texture::TT_3d_texture) {
return fill_d3d_volume_texture_pixels();
}
HRESULT hr = E_FAIL;
nassertr(IS_VALID_PTR(get_texture()), E_FAIL);
CPTA_uchar image = get_texture()->get_ram_image();
if (image.is_null()) {
// The texture doesn't have an image to load. That's ok; it
// might be a texture we've rendered to by frame buffer
// operations or something.
return S_OK;
}
PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector);
nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL);
DWORD orig_width = (DWORD) get_texture()->get_x_size();
DWORD orig_height = (DWORD) get_texture()->get_y_size();
DWORD orig_depth = (DWORD) get_texture()->get_z_size();
DWORD num_color_channels = get_texture()->get_num_components();
D3DFORMAT source_format = _d3d_format;
BYTE *image_pixels = (BYTE*)image.p();
int component_width = get_texture()->get_component_width();
nassertr(IS_VALID_PTR(image_pixels), E_FAIL);
IDirect3DSurface8 *mip_level_0 = NULL;
bool using_temp_buffer = false;
BYTE *pixels = NULL;
for (unsigned int di = 0; di < orig_depth; di++) {
pixels = image_pixels + di * get_texture()->get_expected_ram_page_size();
mip_level_0 = NULL;
if (get_texture()->get_texture_type() == Texture::TT_cube_map) {
nassertr(IS_VALID_PTR(_d3d_cube_texture), E_FAIL);
hr = _d3d_cube_texture->GetCubeMapSurface((D3DCUBEMAP_FACES)di, 0, &mip_level_0);
} else {
nassertr(IS_VALID_PTR(_d3d_2d_texture), E_FAIL);
hr = _d3d_2d_texture->GetSurfaceLevel(0, &mip_level_0);
}
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels failed for " << get_texture()->get_name()
<< ", GetSurfaceLevel failed" << D3DERRORSTRING(hr);
return E_FAIL;
}
RECT source_size;
source_size.left = source_size.top = 0;
source_size.right = orig_width;
source_size.bottom = orig_height;
UINT source_row_byte_length = orig_width * num_color_channels;
DWORD level_0_filter, mip_filter_flags;
using_temp_buffer = false;
// need filtering if size changes, (also if bitdepth reduced (need
// dithering)??)
level_0_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER; //dithering looks ugly on i810 for 4444 textures
// D3DXLoadSurfaceFromMemory will load black luminance and we want
// full white, so convert to explicit luminance-alpha format
if (_d3d_format == D3DFMT_A8) {
// alloc buffer for explicit D3DFMT_A8L8
USHORT *temp_buffer = new USHORT[orig_width * orig_height];
if (!IS_VALID_PTR(temp_buffer)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
goto exit_FillDDSurf;
}
using_temp_buffer = true;
USHORT *out_pixels = temp_buffer;
BYTE *source_pixels = pixels + component_width - 1;
for (UINT y = 0; y < orig_height; y++) {
for (UINT x = 0;
x < orig_width;
x++, source_pixels += component_width, out_pixels++) {
// add full white, which is our interpretation of alpha-only
// (similar to default adding full opaque alpha 0xFF to
// RGB-only textures)
*out_pixels = ((*source_pixels) << 8 ) | 0xFF;
}
}
source_format = D3DFMT_A8L8;
source_row_byte_length = orig_width * sizeof(USHORT);
pixels = (BYTE*)temp_buffer;
} else if (component_width != 1) {
// Convert from 16-bit per channel (or larger) format down to
// 8-bit per channel. This throws away precision in the
// original image, but dx8 doesn't support high-precision images
// anyway.
int num_components = get_texture()->get_num_components();
int num_pixels = orig_width * orig_height * num_components;
BYTE *temp_buffer = new BYTE[num_pixels];
if (!IS_VALID_PTR(temp_buffer)) {
dxgsg8_cat.error() << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
goto exit_FillDDSurf;
}
using_temp_buffer = true;
BYTE *source_pixels = pixels + component_width - 1;
for (int i = 0; i < num_pixels; i++) {
temp_buffer[i] = *source_pixels;
source_pixels += component_width;
}
pixels = (BYTE*)temp_buffer;
}
// filtering may be done here if texture if targetsize != origsize
#ifdef DO_PSTATS
GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_row_byte_length * orig_height);
#endif
hr = D3DXLoadSurfaceFromMemory
(mip_level_0, (PALETTEENTRY*)NULL, (RECT*)NULL, (LPCVOID)pixels,
source_format, source_row_byte_length, (PALETTEENTRY*)NULL,
&source_size, level_0_filter, (D3DCOLOR)0x0);
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels failed for " << get_texture()->get_name()
<< ", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr);
goto exit_FillDDSurf;
}
if (_has_mipmaps) {
if (!dx_use_triangle_mipgen_filter) {
mip_filter_flags = D3DX_FILTER_BOX;
} else {
mip_filter_flags = D3DX_FILTER_TRIANGLE;
}
// mip_filter_flags| = D3DX_FILTER_DITHER;
hr = D3DXFilterTexture(_d3d_texture, (PALETTEENTRY*)NULL, 0,
mip_filter_flags);
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels failed for " << get_texture()->get_name()
<< ", D3DXFilterTex failed" << D3DERRORSTRING(hr);
goto exit_FillDDSurf;
}
}
if (using_temp_buffer) {
SAFE_DELETE_ARRAY(pixels);
}
RELEASE(mip_level_0, dxgsg8, "FillDDSurf MipLev0 texture ptr", RELEASE_ONCE);
}
return hr;
exit_FillDDSurf:
if (using_temp_buffer) {
SAFE_DELETE_ARRAY(pixels);
}
RELEASE(mip_level_0, dxgsg8, "FillDDSurf MipLev0 texture ptr", RELEASE_ONCE);
return hr;
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::fill_d3d_volume_texture_pixels
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
HRESULT DXTextureContext8::
fill_d3d_volume_texture_pixels() {
HRESULT hr = E_FAIL;
nassertr(IS_VALID_PTR(get_texture()), E_FAIL);
CPTA_uchar image = get_texture()->get_ram_image();
if (image.is_null()) {
// The texture doesn't have an image to load. That's ok; it
// might be a texture we've rendered to by frame buffer
// operations or something.
return S_OK;
}
PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector);
nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL);
nassertr(get_texture()->get_texture_type() == Texture::TT_3d_texture, E_FAIL);
DWORD orig_width = (DWORD) get_texture()->get_x_size();
DWORD orig_height = (DWORD) get_texture()->get_y_size();
DWORD orig_depth = (DWORD) get_texture()->get_z_size();
DWORD num_color_channels = get_texture()->get_num_components();
D3DFORMAT source_format = _d3d_format;
BYTE *image_pixels = (BYTE*)image.p();
int component_width = get_texture()->get_component_width();
nassertr(IS_VALID_PTR(image_pixels), E_FAIL);
IDirect3DVolume8 *mip_level_0 = NULL;
bool using_temp_buffer = false;
BYTE *pixels = image_pixels;
nassertr(IS_VALID_PTR(_d3d_volume_texture), E_FAIL);
hr = _d3d_volume_texture->GetVolumeLevel(0, &mip_level_0);
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels failed for " << get_texture()->get_name()
<< ", GetSurfaceLevel failed" << D3DERRORSTRING(hr);
return E_FAIL;
}
D3DBOX source_size;
source_size.Left = source_size.Top = source_size.Front = 0;
source_size.Right = orig_width;
source_size.Bottom = orig_height;
source_size.Back = orig_depth;
UINT source_row_byte_length = orig_width * num_color_channels;
UINT source_page_byte_length = orig_height * source_row_byte_length;
DWORD level_0_filter, mip_filter_flags;
using_temp_buffer = false;
// need filtering if size changes, (also if bitdepth reduced (need
// dithering)??)
level_0_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER; //dithering looks ugly on i810 for 4444 textures
// D3DXLoadSurfaceFromMemory will load black luminance and we want
// full white, so convert to explicit luminance-alpha format
if (_d3d_format == D3DFMT_A8) {
// alloc buffer for explicit D3DFMT_A8L8
USHORT *temp_buffer = new USHORT[orig_width * orig_height * orig_depth];
if (!IS_VALID_PTR(temp_buffer)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
goto exit_FillDDSurf;
}
using_temp_buffer = true;
USHORT *out_pixels = temp_buffer;
BYTE *source_pixels = pixels + component_width - 1;
for (UINT z = 0; z < orig_depth; z++) {
for (UINT y = 0; y < orig_height; y++) {
for (UINT x = 0;
x < orig_width;
x++, source_pixels += component_width, out_pixels++) {
// add full white, which is our interpretation of alpha-only
// (similar to default adding full opaque alpha 0xFF to
// RGB-only textures)
*out_pixels = ((*source_pixels) << 8 ) | 0xFF;
}
}
}
source_format = D3DFMT_A8L8;
source_row_byte_length = orig_width * sizeof(USHORT);
source_page_byte_length = orig_height * source_row_byte_length;
pixels = (BYTE*)temp_buffer;
} else if (component_width != 1) {
// Convert from 16-bit per channel (or larger) format down to
// 8-bit per channel. This throws away precision in the
// original image, but dx8 doesn't support high-precision images
// anyway.
int num_components = get_texture()->get_num_components();
int num_pixels = orig_width * orig_height * orig_depth * num_components;
BYTE *temp_buffer = new BYTE[num_pixels];
if (!IS_VALID_PTR(temp_buffer)) {
dxgsg8_cat.error() << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
goto exit_FillDDSurf;
}
using_temp_buffer = true;
BYTE *source_pixels = pixels + component_width - 1;
for (int i = 0; i < num_pixels; i++) {
temp_buffer[i] = *source_pixels;
source_pixels += component_width;
}
pixels = (BYTE*)temp_buffer;
}
// filtering may be done here if texture if targetsize != origsize
#ifdef DO_PSTATS
GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_page_byte_length * orig_depth);
#endif
hr = D3DXLoadVolumeFromMemory
(mip_level_0, (PALETTEENTRY*)NULL, (D3DBOX*)NULL, (LPCVOID)pixels,
source_format, source_row_byte_length, source_page_byte_length,
(PALETTEENTRY*)NULL,
&source_size, level_0_filter, (D3DCOLOR)0x0);
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels failed for " << get_texture()->get_name()
<< ", D3DXLoadVolumeFromMem failed" << D3DERRORSTRING(hr);
goto exit_FillDDSurf;
}
if (_has_mipmaps) {
if (!dx_use_triangle_mipgen_filter) {
mip_filter_flags = D3DX_FILTER_BOX;
} else {
mip_filter_flags = D3DX_FILTER_TRIANGLE;
}
// mip_filter_flags| = D3DX_FILTER_DITHER;
hr = D3DXFilterTexture(_d3d_texture, (PALETTEENTRY*)NULL, 0,
mip_filter_flags);
if (FAILED(hr)) {
dxgsg8_cat.error()
<< "FillDDSurfaceTexturePixels failed for " << get_texture()->get_name()
<< ", D3DXFilterTex failed" << D3DERRORSTRING(hr);
goto exit_FillDDSurf;
}
}
exit_FillDDSurf:
if (using_temp_buffer) {
SAFE_DELETE_ARRAY(pixels);
}
RELEASE(mip_level_0, dxgsg8, "FillDDSurf MipLev0 texture ptr", RELEASE_ONCE);
return hr;
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::down_to_power_2
// Access: Private, Static
// Description: Returns the largest power of 2 less than or equal
// to value.
////////////////////////////////////////////////////////////////////
int DXTextureContext8::
down_to_power_2(int value) {
int x = 1;
while ((x << 1) <= value) {
x = (x << 1);
}
return x;
}
////////////////////////////////////////////////////////////////////
// Function: DXTextureContext8::get_bits_per_pixel
// Access: Private
// Description: Maps from the Texture's Format symbols to bpp.
// Returns # of alpha bits. Note: Texture's format
// indicates REQUESTED final format, not the stored
// format, which is indicated by pixelbuffer type
////////////////////////////////////////////////////////////////////
unsigned int DXTextureContext8::
get_bits_per_pixel(Texture::Format format, int *alphbits) {
*alphbits = 0; // assume no alpha bits
switch(format) {
case Texture::F_alpha:
*alphbits = 8;
case Texture::F_color_index:
case Texture::F_red:
case Texture::F_green:
case Texture::F_blue:
case Texture::F_rgb332:
return 8;
case Texture::F_luminance_alphamask:
*alphbits = 1;
return 16;
case Texture::F_luminance_alpha:
*alphbits = 8;
return 16;
case Texture::F_luminance:
return 8;
case Texture::F_rgba4:
*alphbits = 4;
return 16;
case Texture::F_rgba5:
*alphbits = 1;
return 16;
case Texture::F_depth_component:
case Texture::F_rgb5:
return 16;
case Texture::F_rgb8:
case Texture::F_rgb:
return 24;
case Texture::F_rgba8:
case Texture::F_rgba:
case Texture::F_rgbm:
if (format == Texture::F_rgbm) // does this make any sense?
*alphbits = 1;
else *alphbits = 8;
return 32;
case Texture::F_rgb12:
return 36;
case Texture::F_rgba12:
*alphbits = 12;
return 48;
}
return 8;
}