DirectDraw - DDERR_SURFACEBUSY

0

Witam!
Zacząłem ostatnio bawić się nieco DD i wyszystko bylo ok, dopóki nie spróbowałem wczytac bitmapy do IDirectDrawSurface4. Niby bitmapa laduje się do pamięci, potem przepisuje się do obiektu IDirectDrawSurface4, ale jak próbuje narysować tą powierzchnie na buforze to ciągle mam DDERR_SURFACEBUSY. Wszytsko robie tak jak jest w kursie (tyle że przerabiam kod z C). Jeśli ktoś wie co moge robić źle niech poratuje!
hError przechwytuje błędy DD. Kod obslugi błędów wywaliłem, żeby nie zaśmiecać.

procedure LoadBmpToDDS(const BMPFileName : PAnsiChar; out lpDDS : IDIRECTDRAWSURFACE4);
var
  ddsd : TDDSURFACEDESC2;
  bitmapa : HBITMAP;
  bmpS : BITMAP;
  pomoc : HDC;
  hdcc : HDC;
begin
  lpDDS := nil;
  bitmapa := LoadImage(0, BMPFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

  pomoc := CreateCompatibleDC(0);
  SelectObject(pomoc, bitmapa);
  ZeroMemory(@ddsd, SizeOf(ddsd));
  ddsd.dwSize := SizeOf(ddsd);
  ddsd.dwFlags := DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT;
  ddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_SYSTEMMEMORY;

  GetObject(bitmapa, SizeOf(bmpS), @bmpS);
  ddsd.dwWidth := bmpS.bmWidth;
  ddsd.dwHeight := bmpS.bmHeight;

  hError := lpDD4.CreateSurface(ddsd, lpDDS, nil);

  hError := lpDDS.GetDC(hdcc);
  BitBlt(hdcc, 0, 0, bmpS.bmWidth, bmpS.bmHeight, pomoc, 0, 0, SRCCOPY);

  hError := lpDDS.ReleaseDC(hdcc);

 DeleteDC(pomoc);
 DeleteDC(hdcc);
 DeleteObject(bitmapa);
end;

I rysowanie na buforze

lpDDSBufor.Blt(0, 0, lpDDSbmp, nil, DDBLTFAST_WAIT);
0
  1. DDBLTFAST_WAIT jest do BltFast. Użyj DDBL_WAIT (pewnie i tak mają tę samą wartość, ale chodzi o zasadę).
  2. DirectDraw samo w sobie jest deprecated, ale jak już, to czemu nie użyjesz ostatniej wersji, DD7? Samo to może ci rozwiązać problem... (do DD7 trzeba użyć DirectDrawCreateEx).

I rysowanie na buforze
Nie podałeś co to za bufor, jak powstaje, a to może wyjaśnić dlaczego jest busy.

0

Sorki tam mała literowka. Używam BltFast.

  dds_hdc                 : HDC;                // HDC(kontekst) buforu
  hdcmem                  : HDC;                // kolejny kontekst ;)
  lpDDSEkran, lpDDSBufor  : IDIRECTDRAWSURFACE4;
  lpDD                    : IDirectDraw;        // obiekt DirectDraw
  lpDD4                   : IDirectDraw4;       // obiekt DirectDraw 4

Funkcja tworzenia DD:

function InitDirectDraw( h_Wnd : HWND ) : boolean;
var
  ddsd : TDDSURFACEDESC2;
begin
  Result := true;
  DirectDrawCreate(nil, lpDD, nil) <> DD_OK;
  lpDD.QueryInterface(IID_IDirectDraw4,lpDD4);

  lpDD4.SetCooperativeLevel(h_Wnd, DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN or DDSCL_ALLOWREBOOT);
  lpDD4.SetDisplayMode(DD_Width, DD_Height, DD_bDepth, 0, 0);

  ZeroMemory(@ddsd,sizeof(ddsd));
  ddsd.dwSize := sizeof(ddsd);
  ddsd.dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;
  ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE
                        or DDSCAPS_FLIP or DDSCAPS_COMPLEX;
  ddsd.dwBackBufferCount := 1;
  lpDD4.CreateSurface(ddsd,lpDDSEkran, nil);
  ddsd.ddsCaps.dwCaps := DDSCAPS_BACKBUFFER;
  lpDDSEkran.GetAttachedSurface(ddsd.ddsCaps, lpDDSBufor);
  hdcmem := CreateCompatibleDC(0);
end;

A na razie korzystam z DD4 bo jak pisałem robie na podstawie kursu a tam jest na DD4. Ale jeśli DD7 może rozwiązać problem to się przerzuce.

0

Już rozgryzłem. Sam sobie blokowałem surface funkcja lpDDS.GetDC... Ale teraz z kolei nie moge dojść jak ustawić kolor np czyszczenia ekranu czy ColorKey dla powierzchni. Próbowałem

$FF00FF, $00FF00FF, RGB(255, 0, 255)

ale kolor jaki z tego wychodzi jest zupełnie inny... Jakieś pomysły?

0

A jaką masz głębię kolorów ekranu? Bo DD_bDepth nic nie mówi.

Dla 24 bitów to będzie albo RGB albo BGR.
Dla 32 bitów masz możliwości: 0RGB, RGB0, 0BGR, BGR0.

Nie ma wiele możliwości, kilka prób i będziesz wiedział.

Najlepiej znajdź gdzie jest czerwony: $FF000000, $00FF0000, $0000FF00 czy $000000FF.

$FF00FF, $00FF00FF
Te dwie liczby są sobie oczywiście równe.

PS. do wczytywania obrazów polecam zapoznać się z biblioteką WIC (Windows Imaging Component). Obsługuje wiele formatów (jpg, png, ...), jest bardziej zrozumiała w użyciu od HBITMAP i ferajny, i działa na interfejsach podobnie jak DirectX.
Nie wiem jednak jak jest z dostępnością unitu do WIC pod Delphi.

0

Chyba z tym WIC pod delphi bedzie problem... Co do glebi 24 bity to program mi tego "nie uznaje". Co prawda uruchamia sie ale w rozdzielczosci ekranu (a ma sie uruchomic w 800x600) i jak klikne myszka to sie wysypuje. Przy 8, 16 i 32 jest wszystko ok. Poza tym ciezko mi idzie z debugowaniem bo nie bardzo wiem jak uruchomic program w oknie, a na fullscreen nie moge sie przelaczyc na delphi przy breakpoincie.

0

Chyba z tym WIC pod delphi bedzie problem...
Kwestia napisania unitu importującego interfejsy. Pewnie ktoś już to zrobił, pogóglaj.

bo nie bardzo wiem jak uruchomic program w oknie

SetCooperativeLevel(DDSCL_NORMAL).
Nie robisz SetDisplayMode, przy tworzeniu powierzchni ekranu nie podajesz DDSD_BACKBUFFERCOUNT, DDSCAPS_FLIP ani DDSCAPS_COMPLEX.
Nie robisz GetAttachedSurface, tylko tworzysz drugą powierzchnię o rozmiarze okna (GetClientRect).
Tworzysz clippera (CreateClipper, SetHWnd, SetClipper) do głównej powierzchni.
Pamiętaj że główna powierzchnia nadal wskazuje CAŁY ekran, z całą jego rozdzielczością i głębią kolorów.
Rysujesz na drugiej powierzchni, ale nie robisz Flip tylko Blt do powierzchni podstawowej (ale na pozycję okna, nie na współrzędne 0,0 - patrz zdanie wyżej).

0

Dziala jak rzekles. Tylko nadal mnie zastanawia co jest nie tak z ta glebia 24bity... I jeszcze taka rzecz - jak narysowac polprzezroczysta powierzchnie? Bo nie wiem za bardzo jak to ugryzc a w zadnym tutku ktory znalazlem tego nie ma. Z gory dzieki :)

0

Karta graficzna może ci po prostu nie obsługiwać 24-bitowego koloru.

Jeśli przez półprzezroczystość masz na myśli wzajemne przenikanie obiektów, to DD nie ma takiego mechanizmu wbudowanego. Wszystko da się oczywiście zasymulować...

0

A jak to + - zrobic w DD? Cos ala polprzezroczystosc w vcl w TBitmap? Czy jakos inaczej sie da?

Ale to by troche dziwne bylo ze nie ma 24bitow a 32 sa...

0

Kurcze, ze nie ma zadnego dobrego kursu o DD...
Znowu pojawil sie problem ktorego wczesniej nie zauwazylem...
Na poczatku chcialem zwalniac wszystkie obiekty DD w WM_QUIT ale jakos sie nie zwalnialy (zrobilem log i nie wpisalo ze rozpoczeto zwalnianie obiektow DD), wiec wrzucilem ten kod za petle komunikatow. No i tu jest szkopul: zwalniam wszystko w odwrotnej kolejnosci niz jest tworzone a ciagle mi wyrzuca Runtime error 216... Probowalem komentowac na wszystkie chyba sposoby i nic... ograniczylem sie tylko do inicjalizacji DD i tez nic... ciagle error 216. Czy to mozliwe, ze DD samoistnie zwalnia wszystkie obiekty po wyjsciu z programu?
Zwalniajac najpierw sprawdzam czy np lpDD7 <> nil.

0
function InitDirectDraw(const h_Wnd : HWND ) : boolean;
var
  ddsd : TDDSURFACEDESC2;
begin
  Result := true;

  if DirectDrawCreate(nil, lpDD, nil) <> DD_OK then
    begin
      Result := false;
      Exit;
    end;

  lpDD.QueryInterface(IID_IDirectDraw7, lpDD7);
  if lpDD7.SetCooperativeLevel(h_Wnd, DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN) <> DD_OK then
    MessageBox(0, 'Blad SCL', Blad', 0);
  if lpDD7.SetDisplayMode(DD_Width, DD_Height, DD_bDepth, 0, 0) <> DD_OK then
    MessageBox(0, 'Blad SDM', Blad', 0);
        
  ZeroMemory(@ddsd, SizeOf(ddsd));
  ddsd.dwSize := SizeOf(ddsd);
  ddsd.dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;
  ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE or DDSCAPS_FLIP or DDSCAPS_COMPLEX;
  ddsd.dwBackBufferCount := 1;
  if lpDD7.CreateSurface(ddsd, lpDDSEkran, nil) <> DD_OK then
    begin
      Result := false;
      Exit;
    end;
  ddsd.ddsCaps.dwCaps := DDSCAPS_BACKBUFFER;
  lpDDSEkran.GetAttachedSurface(ddsd.ddsCaps, lpDDSBufor);
  lpDD7.CreateClipper(0, lpDDC, nil);
  lpDDC.SetHWnd(0, h_Wnd);
  lpDDSBufor.SetClipper(lpDDC);   
end;

procedure RelaseDD;
begin
  if lpDD = nil then Exit;

  if lpDDSBufor.SetClipper(nil) <> DD_OK then
    MessageBox(hInstance, 'Blad zwalniania clipera dla powierzchni', 'Błąd',0);

  if lpDDC <> nil then
  if lpDDC._Release <> DD_OK then
    MessageBox(0, 'Blad zwalniania clipera', 'Błąd',0);
  lpDDC := nil;



  if lpDDSEkran <> nil then
  if lpDDSEkran._Release <> DD_OK then
    MessageBox(0, 'Blad zwalniania lpDDSEkran', 'Błąd',0);
  lpDDSEkran := nil;

  if lpDD7 <> nil then
  if lpDD7._Release <> DD_OK then
    MessageBox(0, 'Blad zwalniania lpDD7', 'Błąd',0);
  lpDD7 := nil;                    

  if lpDD <> nil then
  if lpDD._Release <> DD_OK then
    MessageBox(0, 'Blad zwalniania lpDD', 'Błąd',0);
  lpDD := nil;

end; 
0

Wywal wszystkie _Release. Interfejsy w Delphi mają automatyczne zliczanie referencji, i są zwalniane kiedy zmienna wychodzi z zasięgu. Ewentualnie możesz zrobić samo przypisanie do nil, to też zwalnia.

Błąd masz właśnie dlatego, że :=nil próbuje wywołać _Release na zwolnionym już obiekcie.

0

Niestety po przetestowaniu chyba nie do konca pomoglo... Nadal mam error 216...

1 użytkowników online, w tym zalogowanych: 0, gości: 1