CPU-GPU 메모리 데이터 흐름 [닫힘]


16

저는 초보자 그래픽 프로그래머이며 최근 궁금해 왔습니다. 모델 데이터 (메시 및 재료)가 응용 프로그램 (CPU 메모리)에서 그래픽 카드 (GPU 메모리)로 어떻게 이동합니까? 한 번로드하고 설정 한 정적 모델 (예 : 건물)이 있고 앱 수명 동안 변경되지 않는다고 가정 해 보겠습니다.

  • 데이터가 한 번만 GPU 메모리로 전송되어 영원히 앉아 있습니까?
  • 모델이 실제로 각 프레임을 렌더링 할 때 GPU 프로세서는 GPU 메모리에서 매번 데이터를 가져와야합니까? 내가 의미하는 것은-두 번의 모델을 각각 여러 번 렌더링 한 경우-첫 번째 모델을 여러 번 렌더링 한 다음 두 번째 모델을 여러 번 렌더링하거나 첫 번째 모델을 한 번만 렌더링하면 두 번째 모델은 한 번만 렌더링하면 중요합니다. 계속 그렇게 인터리브? 이런 의미에서이 질문을 "내부 GPU 데이터 흐름"이라고 부를 수 있습니다.
  • 분명히 그래픽 카드는 RAM이 제한적입니다 .1 프레임을 렌더링하는 데 필요한 모든 모델 데이터를 보유 할 수없는 경우 각 프레임마다 CPU RAM에서 가져 오는 (일부) 메모리가 계속 맞습니까?

인터넷에는 이것에 관한 많은 책과 기사가 있지만 알고 있지만이 데이터 흐름을 관리하는 방법에 대한 간단한 일반적인 지침이있을 것입니다 (무엇을 얼마나 많이, 언제, 어떻게 렌더링 할 것인지)?

편집 : 거기 : 나는 하나 개 구별 할 깜빡 GPU에 데이터를 전송 하고있다 현재로 버퍼를 바인딩 / 설정 . 후자가 데이터 흐름을 유발합니까?

편집 2 : Raxvan의 게시물을 읽은 후 몇 가지 작업을 구별하고 싶습니다.

  • 초기화로 버퍼 생성 (CPU 램 또는 GPU 중 하나에 데이터를 저장할 수 있다고 말했듯이)
  • 버퍼 데이터 업데이트 (데이터가 CPU 램에 보관되어있을 때 간단하고 GPU 램에 보관되어있을 때 GPU에서 CPU 램으로 가져오고 다시 가져와야한다고 생각합니다)
  • 버퍼를 활성으로 바인딩합니다 ( 다음 그리기 호출 에서이 버퍼를 렌더링 하고 자체적으로 아무것도하지 않는다고 API에 알리는 방법 입니까?)
  • API 드로우 콜 (여기서 실제로 어떤 일이 발생하는지 듣고 싶습니다)

나는 어떤 방법으로도 전문가는 아니지만 VAO 및 VBO와 함께 최신 OpenGL을 사용하는 경우 glBuffer 명령 제품군 중 하나를 사용할 때마다 데이터가 GPU로 전송되어 VRAM에 저장됩니다. 그런 다음 그릴 때마다 관련 정점이 VRAM에서 가져와 렌더링됩니다. 움직이는 모델 인 경우 정적으로 저장하고 행렬을 사용하여 모델 공간에서 월드 / 카메라 공간으로 이동하는 경향이 있습니다. 마지막으로, RAM이 부족하면 어떻게 될지 모르겠습니다. 내 생각에 VRAM이 부족하면 데이터가 전송되지 않고 오류 코드가 나타날 수 있습니다.
Polar

@Polar-정확하지 않습니다. GL은 실제로 버퍼 객체가 저장되는 메모리를 지정하지 않으며 사용 패턴에 따라 런타임에 자유롭게 이동할 수도 있습니다. GL4.4는이 문제를 다소 다루지 만, 결국 제공 할 수있는 최선의 방법은 "이 바보 같은 힌트 중 하나"입니다. opengl.org/registry/specs/ARB/buffer_storage.txt 및 특히 ​​이슈 2와 9를 참조하십시오.
Maximus Minimus

1
@JimmyShelter 아, 감사합니다. "이 바보 같은 힌트"와보다 구체적인 사양이 적다면 좋을 것입니다.
Polar

@Polar-성가신 것은 ARB_buffer_storage 또 다른 힌트를 포함하여 피할 있었지만 디자이너는 기회를 놓쳤다는 것입니다. 어쩌면 4.5가 마침내 맞을 것입니다.
Maximus Minimus

2
답변에 "응답"하도록 질문을 편집하지 마십시오. 대신 새로운 질문을 게시하십시오.

답변:


12

데이터가 한 번만 GPU 메모리로 전송되어 영원히 앉아 있습니까?

일반적으로 그렇습니다. 그러나 드라이버는 "최적의"작업을 자유롭게 수행 할 수 있으며, 데이터가 VRAM 또는 RAM에 저장되거나 여기에 캐시 될 수 있습니다. 실제로 VBO 흐름 에서 실제로 발생하는 상황을 설명하는 과정이 있습니다.

예를 들어, 동적 openGL 버퍼 (예 : VBO)로 플래그가 지정된 경우 RAM에 저장 될 가능성이 높습니다. GPU는 CPU의 개입없이 직접 메모리 액세스 (DMA)를 사용하여 램에 직접 액세스합니다. 이는 그래픽 카드 및 그래픽 드라이버의 DMA 컨트롤러에 의해 제어되며 커널 모드에서 실행됩니다.

모델이 실제로 각 프레임을 렌더링 할 때 모델이 여러 차례 순차적으로 렌더링 되더라도 GPU 프로세서는 GPU 메모리에서 매번 데이터를 가져와야합니까?

CPU와 마찬가지로 GPU는 GPU 명령 및 메모리 액세스 작업의 순서다시 지정할 수 있으므로 (읽기 : 순서가 잘못됨 ) 대부분 캐시에있는 메모리에 액세스하여 언급 한 시나리오를 처리 할 가능성이 높습니다. ), 때로는이 작업을 수행 할 수 없습니다.

분명히 그래픽 카드는 RAM이 제한적입니다 .1 프레임을 렌더링하는 데 필요한 모든 모델 데이터를 보유 할 수없는 경우 각 프레임마다 CPU RAM에서 가져 오는 (일부) 메모리가 계속 맞습니까?

이런 일이 일어나지 않기를 바랍니다. 그러나 그 일이 발생하더라도 GPU는 RAM과 VRAM간에 메모리를 이동하기 시작합니다 (GPU의 명령 프로세서가이를 담당합니다). 그러면 렌더링이 훨씬 느려져 GPU가 멈추게됩니다. 데이터를 기다려야하기 때문입니다. V / RAM에서 / V / RAM으로 복사됩니다.

GPU로 데이터를 보내고 버퍼를 현재로 설정 / 바인딩합니다. 후자가 데이터 흐름을 유발합니까?

GPU는 명령 버퍼를 포함 하고 모든 API 명령이이 버퍼에 제출됩니다. 이는 GPU로 복사되는 데이터와 동시에 발생할 수 있습니다. 명령 링 버퍼는 GPU와 CPU 사이의 통신 큐이고 그것이 GPU에 의해 execulated 수 있도록 요구 큐에 제출할 필요가 실행될 수 있는지, 어떤 명령. 새 버퍼를 바인딩하는 작업과 마찬가지로 새 버퍼를 gpu에 제출해야 메모리 위치에 액세스 할 수 있습니다.

이것이 glBegin / glEnd가 더 이상 사용되지 않는 이유 중 하나입니다. 새로운 명령을 제출하려면 큐 동기화 (메모리 펜스 / 배리어 사용)가 필요합니다.

여기에 이미지 설명을 입력하십시오

다른 포인트는 다음과 같습니다.

초기화를 통한 버퍼 생성

초기화하지 않고 버퍼를 할당하고 나중에 사용하기 위해 유지할 수 있습니다. 또는 버퍼를 할당하고 동시에 데이터를 복사 할 수 있습니다 (API 레벨에 대한 대화).

버퍼 데이터 업데이트

glMapBuffer를 사용하여 GPU 측의 메모리를 업데이트 할 수 있습니다. 메모리를 RAM에서 / RAM으로 복사할지 여부는 실제로 표준의 기준이 아니며 공급 업체, GPU 유형 및 드라이버에 따라 크게 다릅니다.

API 그리기 호출 (여기서 실제로 어떤 일이 발생하는지 듣고 싶습니다).

주요 질문의 두 번째 요점은 이것을 다룹니다.

버퍼를 활성으로 바인딩 (이 버퍼를 다음 그리기 호출에서 렌더링하고 자체적으로 아무것도하지 않음을 API에 알리는 방법입니까?)

바인딩이 this객체 지향 언어에서 포인터 를 사용하는 것으로 생각하십시오. 엄격하게 같지는 않지만 결과적인 API 호출은 해당 바인딩 버퍼와 관련이 있습니다.


3

일반적으로 cpu와 gpu의 경계와 참여는 플랫폼에 따라 다르지만 대부분이 모델을 따릅니다 .cpu에는 램, gpu가 있으며 메모리를 이동할 수 있습니다 (일부 경우 램은 공유되지만 단순성을 위해 램을 분리하십시오.)

첫 번째 포인트 : 초기화하는 데이터는 CPU 램 또는 GPU 램에 보관하도록 선택할 수 있으며 두 가지 장점이 있습니다. 무언가를 렌더링 할 때 GPU는 무거운 작업을 수행해야하므로 이미 GPU 메모리에있는 데이터가 더 나은 성능을 제공 할 것입니다. CPU의 경우 먼저 데이터를 GPU로 전송 한 다음 (한동안 유지하도록 선택할 수 있음) 렌더링을 수행해야합니다.

두 번째 요점 : 렌더링에는 많은 트릭이 있지만 주된 방법은 다각형입니다. 프레임에서 GPU는 다각형으로 만든 객체를 하나씩 렌더링하고 GPU가 그림을 디스플레이로 전송하는 것을 완료 한 후 렌더링합니다. 객체와 같은 개념은 없으며 다각형 만 있으며이를 조합하여 이미지를 만듭니다. GPU의 역할은 3D에서 2D로 다각형을 투영하고 원하는 경우 효과를 적용하는 것입니다. 다각형은 CPU-> GPU-> SCREEN 또는 GPU-> SCREEN으로 만 이동합니다 (다각형이 이미 GPU 램에있는 경우)

세 번째 요점 : 애니메이션을 렌더링 할 때는 데이터를 CPU에 가깝게 유지하는 것이 좋습니다. 데이터가 많이 들기 때문에 GPU에 데이터를 유지하고 CPU로 이동 한 후 모든 프레임으로 되 돌리는 것이 가장 좋습니다. 계산해야 할 이와 같은 다른 많은 예제가 있지만 일반적으로 모든 데이터는 계산을 수행하는 사람과 가깝게 유지됩니다. 일반적으로 성능을 얻으려면 최대한 많은 데이터를 GPU 램으로 옮기고 싶을 것입니다.

gpu로 데이터를 실제로 보내는 것은 사용하는 API (directx / opengl 또는 기타)에 의해 수행되며 바인딩과 같은 개념은 추상화이므로 API가 원하는 것을 이해하도록합니다.

편집을 위해 편집하십시오.

  • buffer creation with initialisation: int a = new int[10]a[0] = 0,a[1] = 1.... etc 버퍼를 만들 때 데이터를위한 공간을 만들고 데이터를 초기화 할 때 원하는 것을 넣습니다.

  • buffer data update그것이 CPU 램에 있다면 당신은 vertex * vertices그것을 가지고 놀 수 있고, 거기에 없다면 GPU에서 그것을 옮겨야 할 것입니다 vertex * vertices = map(buffer_id);(지도는 GPU에서 CPU 램으로 데이터를 이동시켜야하는 신화 함수이며, 또한 그 반대입니다. buffer_id = create_buffer(vertices);

  • binding the buffer as activebinding렌더링 이라고하는 개념 은 복잡한 과정이며 10000 개의 매개 변수를 가진 함수를 호출하는 것과 같습니다. 바인딩은 어떤 버퍼가 어디로 가는지를 알려주기 위해 사용 된 용어 일뿐입니다. 이 용어 뒤에 진정한 마술은 없으며 버퍼를 변환하거나 이동하거나 재할 당하지 않고 다음 그리기 호출 에서이 버퍼를 사용한다고 드라이버에게 알립니다.

  • API draw call모든 바인딩 및 세팅 버퍼링이 끝나면 고무가 도로를 만나는 곳입니다. 드로우 콜은 지정한 모든 데이터 (또는 데이터를 가리키는 ID)를 가져 와서 GPU에 보내고 (필요한 경우) GPU에 숫자 크런치를 시작하도록 지시합니다. 이것은 모든 플랫폼에서 완전히 사실이 아니지만 많은 차이점이 있지만, 단순하게 유지하기 위해 GPU에게 .... draw를 알려줍니다.


2

가장 올바른 대답은 프로그래밍 방법에 따라 다르지만 걱정할만한 것입니다. GPU가 엄청나게 빨라졌지만 GPU RAM과의 대역폭은 크지 않으며 가장 실망스러운 병목이 될 것입니다.

데이터가 한 번만 GPU 메모리로 전송되어 영원히 앉아 있습니까?

바라건대 요 렌더링 속도를 위해 매 프레임마다 데이터를 다시 보내지 않고 최대한 많은 데이터를 GPU에 저장하려고합니다. VBO는이 정확한 목적을 제공합니다. 정적 및 동적 VBO는 정적 모델에 가장 적합하고, VBO는 정점이 매 프레임마다 변경되는 모델 (예 : 파티클 시스템)에 가장 적합합니다. 그러나 동적 VBO의 경우에도 매 프레임마다 모든 정점을 다시 보내고 싶지는 않습니다. 변화하는 것만

건물의 경우 정점 데이터가 그대로 있고 행렬 (모델 / 세계, 투영 및 뷰) 만 변경됩니다.

파티클 시스템의 경우, 해당 시스템에 존재할 수있는 최대 파티클 수를 저장할만큼 충분한 동적 VBO를 만들었습니다. 각 프레임마다 해당 프레임에서 방출되는 파티클에 대한 데이터를 몇 개의 유니폼과 함께 보내면됩니다. 그릴 때 해당 VBO에서 시작점과 끝점을 지정할 수 있으므로 파티클 데이터를 삭제할 필요가 없습니다. 난 그냥 그리지 말라고 말할 수 있습니다.

모델이 실제로 각 프레임을 렌더링 할 때 GPU 프로세서는 GPU 메모리에서 매번 데이터를 가져와야합니까? 내가 의미하는 것은-두 번의 모델을 각각 여러 번 렌더링 한 경우-첫 번째 모델을 여러 번 렌더링 한 다음 두 번째 모델을 여러 번 렌더링하거나 첫 번째 모델을 한 번만 렌더링하면 두 번째 모델은 한 번만 렌더링하면 중요합니다. 계속 그렇게 인터리브?

하나 대신 여러 개의 드로우 콜을 보내는 행위는 훨씬 더 큰 한계입니다. 인스턴스화 된 렌더링을 확인하십시오. 도움이 많이되고이 질문에 대한 답을 쓸모 없게 만들 수 있습니다. 아직 해결하지 못한 드라이버 문제가 있었지만 작동시킬 수 있으면 문제가 해결되었습니다.

분명히 그래픽 카드는 RAM이 제한적입니다 .1 프레임을 렌더링하는 데 필요한 모든 모델 데이터를 보유 할 수없는 경우 각 프레임마다 CPU RAM에서 가져 오는 (일부) 메모리가 계속 맞습니까?

GPU RAM이 부족하지 않습니다. 그렇다면, 변경하지 않도록 변경하십시오. 매우 가설적인 시나리오에서 아마 어쩌면 충돌 할 것입니다. 그러나 나는 그것이 일어나는 것을 본 적이 없으므로 솔직히 알 수 없습니다.

한 가지 구별하는 것을 잊었습니다 .GPU로 데이터를 보내고 버퍼를 현재로 설정 / 바인딩합니다. 후자가 데이터 흐름을 유발합니까?

중요한 데이터 흐름은 없습니다. 약간의 비용이 들지만, 작성한 모든 코드 줄에 해당됩니다. 비용이 얼마나 드는지 알아 내면 프로파일 링이 무엇인지 다시 알 수 있습니다.

초기화를 통한 버퍼 생성

Raxvan의 답변은 좋지만 정확하지는 않습니다. OpenGL에서 버퍼를 만들면 공간이 예약 되지 않습니다 . 데이터를 전달하지 않고 공간을 예약하려면 glBufferData를 호출하고 null 만 전달하면됩니다. ( 여기 노트 섹션을 참조 하십시오 .)

버퍼 데이터 업데이트

glBufferData 또는 다른 기능을 의미한다고 생각합니까? 실제 데이터 전송이 이루어지는 곳입니다. (마지막 단락에서 말한 것처럼 null을 전달하지 않으면)

버퍼를 활성 상태로 바인딩 (다음 그리기 호출 에서이 버퍼를 렌더링하고 자체적으로 아무것도하지 않는다고 API에 알리는 방법입니까?)

예,하지만 그보다 조금 더 할 수 있습니다. 예를 들어, VAO (정점 배열 객체)를 바인딩 한 다음 VBO를 바인딩하면 해당 VBO가 VAO에 바인딩됩니다. 나중에 해당 VAO를 다시 바인딩하고 glDrawArrays를 호출하면 어떤 VBO를 그릴 지 알 수 있습니다.

많은 자습서를 통해 모든 VBO에 대해 VAO를 만들 수 있지만 이것이 의도 된 용도가 아니라는 말을 들었습니다. 하나의 VAO를 생성하고 동일한 속성을 가진 모든 VBO와 함께 사용해야합니다. 나는 이것을 아직 시도하지 않았으므로 그것이 더 나은지 아닌지를 확실히 말할 수는 없습니다.

API 드로우 콜

여기서 일어나는 일은 (우리의 관점에서) 매우 간단합니다. VAO를 바인딩하고 glDrawArrays를 호출한다고 가정 해보십시오. 시작점과 개수를 지정하면 해당 범위의 모든 정점에 대해 정점 셰이더가 실행되어 출력이 선 아래로 전달됩니다. 그러나이 모든 과정은 그 자체의 또 다른 에세이입니다.


"문제가 해결되었습니다"그렇습니다. 인스턴스화는 많은 도움이 될 것입니다. 그러나 그것 없이는 여전히 각 객체에 대한 그리기 호출을해야합니다. 두 경우 모두 동일합니다. 순서가 중요한지 궁금합니다.
NPS

@NPS - 그것은 중요한 몇 가지 . 그것들이 주문되어서 바인딩을 계속 바꾸지 않아도된다면, 아마도 그 정도는 더 빠를 것입니다. 그러나 정렬 방식을 벗어나야한다면 아마도 훨씬 더 많은 비용이들 것입니다. 구현에 따라 너무 많은 변수가 너무 많습니다.
Icy Defiance
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.