PageRenderTime 58ms CodeModel.GetById 26ms app.highlight 28ms RepoModel.GetById 0ms app.codeStats 0ms

/Irrlicht/CD3D9Texture.cpp

http://myjeh.googlecode.com/
C++ | 699 lines | 533 code | 113 blank | 53 comment | 105 complexity | 3a4feca75b378d87f7f5e5e7914473ef MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0
  1// Copyright (C) 2002-2010 Nikolaus Gebhardt
  2// This file is part of the "Irrlicht Engine".
  3// For conditions of distribution and use, see copyright notice in irrlicht.h
  4
  5#include "IrrCompileConfig.h"
  6#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
  7
  8#define _IRR_DONT_DO_MEMORY_DEBUGGING_HERE
  9#include "CD3D9Texture.h"
 10#include "CD3D9Driver.h"
 11#include "os.h"
 12
 13#include <d3dx9tex.h>
 14
 15#ifndef _IRR_COMPILE_WITH_DIRECT3D_8_
 16// The D3DXFilterTexture function seems to get linked wrong when
 17// compiling with both D3D8 and 9, causing it not to work in the D3D9 device.
 18// So mipmapgeneration is replaced with my own bad generation in d3d 8 when
 19// compiling with both D3D 8 and 9.
 20// #define _IRR_USE_D3DXFilterTexture_
 21#endif // _IRR_COMPILE_WITH_DIRECT3D_8_
 22
 23#ifdef _IRR_USE_D3DXFilterTexture_
 24#pragma comment(lib, "d3dx9.lib")
 25#endif
 26
 27namespace irr
 28{
 29namespace video
 30{
 31
 32//! rendertarget constructor
 33CD3D9Texture::CD3D9Texture(CD3D9Driver* driver, const core::dimension2d<u32>& size,
 34						   const io::path& name, const ECOLOR_FORMAT format)
 35: ITexture(name), Texture(0), RTTSurface(0), Driver(driver), DepthSurface(0),
 36	TextureSize(size), ImageSize(size), Pitch(0), ColorFormat(ECF_UNKNOWN),
 37	HasMipMaps(false), HardwareMipMaps(false), IsRenderTarget(true)
 38{
 39	#ifdef _DEBUG
 40	setDebugName("CD3D9Texture");
 41	#endif
 42
 43	Device=driver->getExposedVideoData().D3D9.D3DDev9;
 44	if (Device)
 45		Device->AddRef();
 46
 47	createRenderTarget(format);
 48}
 49
 50
 51//! constructor
 52CD3D9Texture::CD3D9Texture(IImage* image, CD3D9Driver* driver,
 53			   u32 flags, const io::path& name, void* mipmapData)
 54: ITexture(name), Texture(0), RTTSurface(0), Driver(driver), DepthSurface(0),
 55	TextureSize(0,0), ImageSize(0,0), Pitch(0), ColorFormat(ECF_UNKNOWN),
 56	HasMipMaps(false), HardwareMipMaps(false), IsRenderTarget(false)
 57{
 58	#ifdef _DEBUG
 59	setDebugName("CD3D9Texture");
 60	#endif
 61
 62	HasMipMaps = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
 63
 64	Device=driver->getExposedVideoData().D3D9.D3DDev9;
 65	if (Device)
 66		Device->AddRef();
 67
 68	if (image)
 69	{
 70		if (createTexture(flags, image))
 71		{
 72			if (copyTexture(image))
 73			{
 74				regenerateMipMapLevels(mipmapData);
 75			}
 76		}
 77		else
 78			os::Printer::log("Could not create DIRECT3D9 Texture.", ELL_WARNING);
 79	}
 80}
 81
 82
 83//! destructor
 84CD3D9Texture::~CD3D9Texture()
 85{
 86	if (Texture)
 87		Texture->Release();
 88
 89	if (RTTSurface)
 90		RTTSurface->Release();
 91
 92	// if this texture was the last one using the depth buffer
 93	// we can release the surface. We only use the value of the pointer
 94	// hence it is safe to use the dropped pointer...
 95	if (DepthSurface)
 96	{
 97		if (DepthSurface->drop())
 98			Driver->removeDepthSurface(DepthSurface);
 99	}
100
101	if (Device)
102		Device->Release();
103}
104
105
106void CD3D9Texture::createRenderTarget(const ECOLOR_FORMAT format)
107{
108	// are texture size restrictions there ?
109	if(!Driver->queryFeature(EVDF_TEXTURE_NPOT))
110	{
111		if (TextureSize != ImageSize)
112			os::Printer::log("RenderTarget size has to be a power of two", ELL_INFORMATION);
113	}
114	TextureSize = TextureSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT), !Driver->queryFeature(EVDF_TEXTURE_NSQUARE), true, Driver->Caps.MaxTextureWidth);
115
116	D3DFORMAT d3dformat = Driver->getD3DColorFormat();
117
118	if(ColorFormat == ECF_UNKNOWN)
119	{
120		// get irrlicht format from backbuffer
121		// (This will get overwritten by the custom format if it is provided, else kept.)
122		ColorFormat = Driver->getColorFormat();
123		setPitch(d3dformat);
124
125		// Use color format if provided.
126		if(format != ECF_UNKNOWN)
127		{
128			ColorFormat = format;
129			d3dformat = Driver->getD3DFormatFromColorFormat(format);
130			setPitch(d3dformat); // This will likely set pitch to 0 for now.
131		}
132	}
133	else
134	{
135		d3dformat = Driver->getD3DFormatFromColorFormat(ColorFormat);
136	}
137
138	// create texture
139	HRESULT hr;
140
141	hr = Device->CreateTexture(
142		TextureSize.Width,
143		TextureSize.Height,
144		1, // mip map level count, we don't want mipmaps here
145		D3DUSAGE_RENDERTARGET,
146		d3dformat,
147		D3DPOOL_DEFAULT,
148		&Texture,
149		NULL);
150
151	if (FAILED(hr))
152	{
153		if (D3DERR_INVALIDCALL == hr)
154			os::Printer::log("Could not create render target texture", "Invalid Call");
155		else
156		if (D3DERR_OUTOFVIDEOMEMORY == hr)
157			os::Printer::log("Could not create render target texture", "Out of Video Memory");
158		else
159		if (E_OUTOFMEMORY == hr)
160			os::Printer::log("Could not create render target texture", "Out of Memory");
161		else
162			os::Printer::log("Could not create render target texture");
163	}
164}
165
166
167bool CD3D9Texture::createMipMaps(u32 level)
168{
169	if (level==0)
170		return true;
171
172	if (HardwareMipMaps && Texture)
173	{
174		// generate mipmaps in hardware
175		Texture->GenerateMipSubLevels();
176		return true;
177	}
178
179	// manual mipmap generation
180	IDirect3DSurface9* upperSurface = 0;
181	IDirect3DSurface9* lowerSurface = 0;
182
183	// get upper level
184	HRESULT hr = Texture->GetSurfaceLevel(level-1, &upperSurface);
185	if (FAILED(hr) || !upperSurface)
186	{
187		os::Printer::log("Could not get upper surface level for mip map generation", ELL_WARNING);
188		return false;
189	}
190
191	// get lower level
192	hr = Texture->GetSurfaceLevel(level, &lowerSurface);
193	if (FAILED(hr) || !lowerSurface)
194	{
195		os::Printer::log("Could not get lower surface level for mip map generation", ELL_WARNING);
196		upperSurface->Release();
197		return false;
198	}
199
200	D3DSURFACE_DESC upperDesc, lowerDesc;
201	upperSurface->GetDesc(&upperDesc);
202	lowerSurface->GetDesc(&lowerDesc);
203
204	D3DLOCKED_RECT upperlr;
205	D3DLOCKED_RECT lowerlr;
206
207	// lock upper surface
208	if (FAILED(upperSurface->LockRect(&upperlr, NULL, 0)))
209	{
210		upperSurface->Release();
211		lowerSurface->Release();
212		os::Printer::log("Could not lock upper texture for mip map generation", ELL_WARNING);
213		return false;
214	}
215
216	// lock lower surface
217	if (FAILED(lowerSurface->LockRect(&lowerlr, NULL, 0)))
218	{
219		upperSurface->UnlockRect();
220		upperSurface->Release();
221		lowerSurface->Release();
222		os::Printer::log("Could not lock lower texture for mip map generation", ELL_WARNING);
223		return false;
224	}
225
226	if (upperDesc.Format != lowerDesc.Format)
227	{
228		os::Printer::log("Cannot copy mip maps with different formats.", ELL_WARNING);
229	}
230	else
231	{
232		if ((upperDesc.Format == D3DFMT_A1R5G5B5) || (upperDesc.Format == D3DFMT_R5G6B5))
233			copy16BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits,
234					lowerDesc.Width, lowerDesc.Height,
235					upperlr.Pitch, lowerlr.Pitch);
236		else
237		if (upperDesc.Format == D3DFMT_A8R8G8B8)
238			copy32BitMipMap((char*)upperlr.pBits, (char*)lowerlr.pBits,
239					lowerDesc.Width, lowerDesc.Height,
240					upperlr.Pitch, lowerlr.Pitch);
241		else
242			os::Printer::log("Unsupported mipmap format, cannot copy.", ELL_WARNING);
243	}
244
245	bool result=true;
246	// unlock
247	if (FAILED(upperSurface->UnlockRect()))
248		result=false;
249	if (FAILED(lowerSurface->UnlockRect()))
250		result=false;
251
252	// release
253	upperSurface->Release();
254	lowerSurface->Release();
255
256	if (!result || (upperDesc.Width <= 3 && upperDesc.Height <= 3))
257		return result; // stop generating levels
258
259	// generate next level
260	return createMipMaps(level+1);
261}
262
263
264//! creates the hardware texture
265bool CD3D9Texture::createTexture(u32 flags, IImage * image)
266{
267	ImageSize = image->getDimension();
268
269	core::dimension2d<u32> optSize = ImageSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT), !Driver->queryFeature(EVDF_TEXTURE_NSQUARE), true, Driver->Caps.MaxTextureWidth);
270
271	D3DFORMAT format = D3DFMT_A1R5G5B5;
272
273	switch(getTextureFormatFromFlags(flags))
274	{
275	case ETCF_ALWAYS_16_BIT:
276		format = D3DFMT_A1R5G5B5; break;
277	case ETCF_ALWAYS_32_BIT:
278		format = D3DFMT_A8R8G8B8; break;
279	case ETCF_OPTIMIZED_FOR_QUALITY:
280		{
281			switch(image->getColorFormat())
282			{
283			case ECF_R8G8B8:
284			case ECF_A8R8G8B8:
285				format = D3DFMT_A8R8G8B8; break;
286			case ECF_A1R5G5B5:
287			case ECF_R5G6B5:
288				format = D3DFMT_A1R5G5B5; break;
289			}
290		}
291		break;
292	case ETCF_OPTIMIZED_FOR_SPEED:
293		format = D3DFMT_A1R5G5B5;
294		break;
295	default:
296		break;
297	}
298	if (Driver->getTextureCreationFlag(video::ETCF_NO_ALPHA_CHANNEL))
299	{
300		if (format == D3DFMT_A8R8G8B8)
301			format = D3DFMT_R8G8B8;
302		else if (format == D3DFMT_A1R5G5B5)
303			format = D3DFMT_R5G6B5;
304	}
305
306	const bool mipmaps = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
307
308	DWORD usage = 0;
309
310	// This enables hardware mip map generation.
311	if (mipmaps && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
312	{
313		LPDIRECT3D9 intf = Driver->getExposedVideoData().D3D9.D3D9;
314		D3DDISPLAYMODE d3ddm;
315		intf->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
316
317		if (D3D_OK==intf->CheckDeviceFormat(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,d3ddm.Format,D3DUSAGE_AUTOGENMIPMAP,D3DRTYPE_TEXTURE,format))
318		{
319			usage = D3DUSAGE_AUTOGENMIPMAP;
320			HardwareMipMaps = true;
321		}
322	}
323
324	HRESULT hr = Device->CreateTexture(optSize.Width, optSize.Height,
325		mipmaps ? 0 : 1, // number of mipmaplevels (0 = automatic all)
326		usage, // usage
327		format, D3DPOOL_MANAGED , &Texture, NULL);
328
329	if (FAILED(hr))
330	{
331		// try brute force 16 bit
332		HardwareMipMaps = false;
333		if (format == D3DFMT_A8R8G8B8)
334			format = D3DFMT_A1R5G5B5;
335		else if (format == D3DFMT_R8G8B8)
336			format = D3DFMT_R5G6B5;
337		else
338			return false;
339
340		hr = Device->CreateTexture(optSize.Width, optSize.Height,
341			mipmaps ? 0 : 1, // number of mipmaplevels (0 = automatic all)
342			0, format, D3DPOOL_MANAGED, &Texture, NULL);
343	}
344
345	ColorFormat = Driver->getColorFormatFromD3DFormat(format);
346	setPitch(format);
347	return (SUCCEEDED(hr));
348}
349
350
351//! copies the image to the texture
352bool CD3D9Texture::copyTexture(IImage * image)
353{
354	if (Texture && image)
355	{
356		D3DSURFACE_DESC desc;
357		Texture->GetLevelDesc(0, &desc);
358
359		TextureSize.Width = desc.Width;
360		TextureSize.Height = desc.Height;
361
362		D3DLOCKED_RECT rect;
363		HRESULT hr = Texture->LockRect(0, &rect, 0, 0);
364		if (FAILED(hr))
365		{
366			os::Printer::log("Texture data not copied", "Could not LockRect D3D9 Texture.", ELL_ERROR);
367			return false;
368		}
369
370		Pitch = rect.Pitch;
371		image->copyToScaling(rect.pBits, TextureSize.Width, TextureSize.Height, ColorFormat, Pitch);
372
373		hr = Texture->UnlockRect(0);
374		if (FAILED(hr))
375		{
376			os::Printer::log("Texture data not copied", "Could not UnlockRect D3D9 Texture.", ELL_ERROR);
377			return false;
378		}
379	}
380
381	return true;
382}
383
384
385//! lock function
386void* CD3D9Texture::lock(bool readOnly, u32 mipmapLevel)
387{
388	if (!Texture)
389		return 0;
390
391	MipLevelLocked=mipmapLevel;
392	HRESULT hr;
393	D3DLOCKED_RECT rect;
394	if(!IsRenderTarget)
395	{
396		hr = Texture->LockRect(mipmapLevel, &rect, 0, readOnly?D3DLOCK_READONLY:0);
397		if (FAILED(hr))
398		{
399			os::Printer::log("Could not lock DIRECT3D9 Texture.", ELL_ERROR);
400			return 0;
401		}
402	}
403	else
404	{
405		if (!RTTSurface)
406		{
407			// Make RTT surface large enough for all miplevels (including 0)
408			D3DSURFACE_DESC desc;
409			Texture->GetLevelDesc(0, &desc);
410			hr = Device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &RTTSurface, 0);
411			if (FAILED(hr))
412			{
413				os::Printer::log("Could not lock DIRECT3D9 Texture", "Offscreen surface creation failed.", ELL_ERROR);
414				return 0;
415			}
416		}
417
418		IDirect3DSurface9 *surface = 0;
419		hr = Texture->GetSurfaceLevel(mipmapLevel, &surface);
420		if (FAILED(hr))
421		{
422			os::Printer::log("Could not lock DIRECT3D9 Texture", "Could not get surface.", ELL_ERROR);
423			return 0;
424		}
425		hr = Device->GetRenderTargetData(surface, RTTSurface);
426		surface->Release();
427		if(FAILED(hr))
428		{
429			os::Printer::log("Could not lock DIRECT3D9 Texture", "Data copy failed.", ELL_ERROR);
430			return 0;
431		}
432		hr = RTTSurface->LockRect(&rect, 0, readOnly?D3DLOCK_READONLY:0);
433		if(FAILED(hr))
434		{
435			os::Printer::log("Could not lock DIRECT3D9 Texture", "LockRect failed.", ELL_ERROR);
436			return 0;
437		}
438	}
439	return rect.pBits;
440}
441
442
443//! unlock function
444void CD3D9Texture::unlock()
445{
446	if (!Texture)
447		return;
448
449	if (!IsRenderTarget)
450		Texture->UnlockRect(MipLevelLocked);
451	else if (RTTSurface)
452		RTTSurface->UnlockRect();
453}
454
455
456//! Returns original size of the texture.
457const core::dimension2d<u32>& CD3D9Texture::getOriginalSize() const
458{
459	return ImageSize;
460}
461
462
463//! Returns (=size) of the texture.
464const core::dimension2d<u32>& CD3D9Texture::getSize() const
465{
466	return TextureSize;
467}
468
469
470//! returns driver type of texture (=the driver, who created the texture)
471E_DRIVER_TYPE CD3D9Texture::getDriverType() const
472{
473	return EDT_DIRECT3D9;
474}
475
476
477//! returns color format of texture
478ECOLOR_FORMAT CD3D9Texture::getColorFormat() const
479{
480	return ColorFormat;
481}
482
483
484//! returns pitch of texture (in bytes)
485u32 CD3D9Texture::getPitch() const
486{
487	return Pitch;
488}
489
490
491//! returns the DIRECT3D9 Texture
492IDirect3DBaseTexture9* CD3D9Texture::getDX9Texture() const
493{
494	return Texture;
495}
496
497
498//! returns if texture has mipmap levels
499bool CD3D9Texture::hasMipMaps() const
500{
501	return HasMipMaps;
502}
503
504
505void CD3D9Texture::copy16BitMipMap(char* src, char* tgt,
506				   s32 width, s32 height,
507				   s32 pitchsrc, s32 pitchtgt) const
508{
509	for (s32 y=0; y<height; ++y)
510	{
511		for (s32 x=0; x<width; ++x)
512		{
513			u32 a=0, r=0, g=0, b=0;
514
515			for (s32 dy=0; dy<2; ++dy)
516			{
517				const s32 tgy = (y*2)+dy;
518				for (s32 dx=0; dx<2; ++dx)
519				{
520					const s32 tgx = (x*2)+dx;
521
522					SColor c;
523					if (ColorFormat == ECF_A1R5G5B5)
524						c = A1R5G5B5toA8R8G8B8(*(u16*)(&src[(tgx*2)+(tgy*pitchsrc)]));
525					else
526						c = R5G6B5toA8R8G8B8(*(u16*)(&src[(tgx*2)+(tgy*pitchsrc)]));
527
528					a += c.getAlpha();
529					r += c.getRed();
530					g += c.getGreen();
531					b += c.getBlue();
532				}
533			}
534
535			a /= 4;
536			r /= 4;
537			g /= 4;
538			b /= 4;
539
540			u16 c;
541			if (ColorFormat == ECF_A1R5G5B5)
542				c = RGBA16(r,g,b,a);
543			else
544				c = A8R8G8B8toR5G6B5(SColor(a,r,g,b).color);
545			*(u16*)(&tgt[(x*2)+(y*pitchtgt)]) = c;
546		}
547	}
548}
549
550
551void CD3D9Texture::copy32BitMipMap(char* src, char* tgt,
552				   s32 width, s32 height,
553				   s32 pitchsrc, s32 pitchtgt) const
554{
555	for (s32 y=0; y<height; ++y)
556	{
557		for (s32 x=0; x<width; ++x)
558		{
559			u32 a=0, r=0, g=0, b=0;
560			SColor c;
561
562			for (s32 dy=0; dy<2; ++dy)
563			{
564				const s32 tgy = (y*2)+dy;
565				for (s32 dx=0; dx<2; ++dx)
566				{
567					const s32 tgx = (x*2)+dx;
568
569					c = *(u32*)(&src[(tgx*4)+(tgy*pitchsrc)]);
570
571					a += c.getAlpha();
572					r += c.getRed();
573					g += c.getGreen();
574					b += c.getBlue();
575				}
576			}
577
578			a /= 4;
579			r /= 4;
580			g /= 4;
581			b /= 4;
582
583			c.set(a, r, g, b);
584			*(u32*)(&tgt[(x*4)+(y*pitchtgt)]) = c.color;
585		}
586	}
587}
588
589
590//! Regenerates the mip map levels of the texture. Useful after locking and
591//! modifying the texture
592void CD3D9Texture::regenerateMipMapLevels(void* mipmapData)
593{
594	if (mipmapData)
595	{
596		core::dimension2du size = TextureSize;
597		u32 level=0;
598		do
599		{
600			if (size.Width>1)
601				size.Width /=2;
602			if (size.Height>1)
603				size.Height /=2;
604			++level;
605			IDirect3DSurface9* mipSurface = 0;
606			HRESULT hr = Texture->GetSurfaceLevel(level, &mipSurface);
607			if (FAILED(hr) || !mipSurface)
608			{
609				os::Printer::log("Could not get mipmap level", ELL_WARNING);
610				return;
611			}
612			D3DSURFACE_DESC mipDesc;
613			mipSurface->GetDesc(&mipDesc);
614			D3DLOCKED_RECT miplr;
615
616			// lock mipmap surface
617			if (FAILED(mipSurface->LockRect(&miplr, NULL, 0)))
618			{
619				mipSurface->Release();
620				os::Printer::log("Could not lock texture", ELL_WARNING);
621				return;
622			}
623
624			memcpy(miplr.pBits, mipmapData, size.getArea()*getPitch()/TextureSize.Width);
625			mipmapData = (u8*)mipmapData+size.getArea()*getPitch()/TextureSize.Width;
626			// unlock
627			mipSurface->UnlockRect();
628			// release
629			mipSurface->Release();
630		} while (size.Width != 1 || size.Height != 1);
631	}
632	else if (HasMipMaps)
633	{
634		// create mip maps.
635#ifdef _IRR_USE_D3DXFilterTexture_
636		// The D3DXFilterTexture function seems to get linked wrong when
637		// compiling with both D3D8 and 9, causing it not to work in the D3D9 device.
638		// So mipmapgeneration is replaced with my own bad generation
639		HRESULT hr  = D3DXFilterTexture(Texture, NULL, D3DX_DEFAULT, D3DX_DEFAULT);
640		if (FAILED(hr))
641#endif
642		createMipMaps();
643	}
644}
645
646
647//! returns if it is a render target
648bool CD3D9Texture::isRenderTarget() const
649{
650	return IsRenderTarget;
651}
652
653
654//! Returns pointer to the render target surface
655IDirect3DSurface9* CD3D9Texture::getRenderTargetSurface()
656{
657	if (!IsRenderTarget)
658		return 0;
659
660	IDirect3DSurface9 *pRTTSurface = 0;
661	if (Texture)
662		Texture->GetSurfaceLevel(0, &pRTTSurface);
663
664	if (pRTTSurface)
665		pRTTSurface->Release();
666
667	return pRTTSurface;
668}
669
670
671void CD3D9Texture::setPitch(D3DFORMAT d3dformat)
672{
673	switch(d3dformat)
674	{
675	case D3DFMT_X1R5G5B5:
676	case D3DFMT_A1R5G5B5:
677		Pitch = TextureSize.Width * 2;
678	break;
679	case D3DFMT_A8B8G8R8:
680	case D3DFMT_A8R8G8B8:
681	case D3DFMT_X8R8G8B8:
682		Pitch = TextureSize.Width * 4;
683	break;
684	case D3DFMT_R5G6B5:
685		Pitch = TextureSize.Width * 2;
686	break;
687	case D3DFMT_R8G8B8:
688		Pitch = TextureSize.Width * 3;
689	break;
690	default:
691		Pitch = 0;
692	};
693}
694
695
696} // end namespace video
697} // end namespace irr
698
699#endif // _IRR_COMPILE_WITH_DIRECT3D_9_