[C++][OpenCL] image3D an Kernel übergen

Fhnx

Cadet 4th Year
Registriert
Feb. 2010
Beiträge
127
[C++][OpenCL] image3D an Kernel übergeben

Hallo Leute!

Ich bin am verzweifeln. Das Problem:
Ich habe eine sehr großes (> 100MB), eindimensionales Array aus BYTE-Werten (unsigned char), welches ein dreidimensionales Volumen darstellt. Es darf nicht verändert werden und muss an einen Kernel übergeben werden. Da man davon ausgehen kann, dass der PC mehr RAM hat, als die Grafikkarte würde ich gerne das Array in ein Image3D packen und per enqueueMapImage an den Kernel geben. Dabei möchte ich das Flag CL_MEM_USE_HOST_PTR benutzen, um Kopien des Arrays aus Zeit- und Speicherplatzgründen zu vermeiden.
Nun spuckt mir enqueueMapImage dauernd den Fehlercode -30 (CL_INVALID_VALUE ) aus. Ich habe schon alle möglichen Spezifikationen und Google bemüht, aber habe nichts gefunden. Deswegen würde ich mich über Hilfe freuen!
Hier der vereinfachte Code mit einen 3x3x3-Array, der den gleichen Fehler produziert:
Code:
#include "CL\cl.hpp"
#include <iostream>

using namespace std;
using namespace cl;

typedef unsigned char BYTE;
#define STRINGIFY(...) #__VA_ARGS__

const char kernel_source[] = STRINGIFY(
	__kernel void test( __read_only image3d_t vol, sampler_t volSampler, __global int* ret){
		size_t idx = get_global_id(0);
		size_t idy = get_global_id(1);
		size_t idz = get_global_id(2);

		ret[idx + 3* (idy + 3*idz)] = read_imagef(vol, volSampler, (int4)(idx, idy, idz, 0)).w*255;
}
);

std::vector<CommandQueue>	m_Queues;
Program						m_Program;
Context						m_Context;
std::vector<Device>			m_Devices;
Platform					m_Platform;
Kernel						m_TestKnl;
Image3D						m_CLVol;
Sampler						m_VolSampler;

const BYTE img3D[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 };

int main(){
	m_Platform = Platform::get();
	m_Platform.getDevices(CL_DEVICE_TYPE_ALL, &m_Devices);
	m_Context = Context(m_Devices);

	m_Program = Program(m_Context, kernel_source);
	cl_int result = m_Program.build(m_Devices);
	if ( result ){
		cout<<"Error during compilation! ( "<<result<<" )\n";
		STRING_CLASS errstr;
		m_Program.getBuildInfo(m_Devices.front(), CL_PROGRAM_BUILD_LOG, &errstr);
		cout<<errstr.c_str();
		throw -1;
	} else {
		cout<<"OpenCL compiled correctly: "<<result<<endl;
	}

	m_Queues.clear();
	for(int i = 0; i < m_Devices.size(); ++i){
		m_Queues.push_back(CommandQueue(m_Context, m_Devices[i]));
	}
	cout<<"Queue-Size: "<<m_Devices.size()<<endl;

	m_TestKnl = Kernel(m_Program, "test");

	ImageFormat imgform(CL_A, CL_UNORM_INT8);
	cl_int err = CL_SUCCESS;
	m_CLVol = Image3D(
		m_Context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, imgform,
		3, 3, 3, 0, 0, (void*)img3D, &err);
	cout<<"\nVol-Image3D: "<<err<<endl;

	m_VolSampler = Sampler(m_Context, CL_TRUE, CL_ADDRESS_NONE, CL_FILTER_LINEAR, &err);
	cout<<"Vol-Image3D-Sampler: "<<err<<endl;


	DWORD start = 0, end = 0;
	float time;

	start = GetTickCount();
	cl_int retArr[27];

	err = 0;
	Buffer retBuf(m_Context, CL_MEM_WRITE_ONLY, sizeof(cl_int)*27, NULL, &err);
	if(err != 0){
		cout<<"Error PicBuffer: "<<err<<endl;
	}

	err = m_TestKnl.setArg(0, sizeof(cl_mem), &m_CLVol);
	cout<<"Arg 0: "<<err<<endl;
	err = m_TestKnl.setArg(1, m_VolSampler);
	cout<<"Arg 1: "<<err<<endl;
	err = m_TestKnl.setArg(2, retBuf);
	cout<<"Arg 2: "<<err<<endl;

	cl::size_t<3> origin, region;
	origin.push_back(0);
	origin.push_back(0);
	origin.push_back(0);
	region.push_back(3);
	region.push_back(3);
	region.push_back(3);
	::size_t row_pitch = 0;
	::size_t slice_pitch = 0;
	m_Queues[0].enqueueMapImage(m_CLVol, CL_TRUE, CL_MAP_READ, origin, region, &row_pitch, &slice_pitch, NULL, NULL, &err);
	cout<<"enqueue Image-Map: "<<err<<endl;
	err = m_Queues[0].enqueueNDRangeKernel(m_TestKnl, NullRange, NDRange(3, 3, 3), NullRange, NULL, NULL);
	cout<<"enqueue Kernel: "<<err<<endl;

	err = m_Queues[0].enqueueReadBuffer(retBuf, CL_TRUE, 0, sizeof(cl_int)*27, retArr);
	cout<<"enqueue retBuf: "<<err<<endl;

	end = GetTickCount();
	time = ((float)(end-start)/1000);
	cout<<"Time: "<<time<<" s\n";

	for(int i = 0; i < 27; ++i){
		cout<<retArr[i]<<" ";
	}
	cout<<endl<<endl;
	cin>>err;
	return 0;
}

Edit: Im Kernel einen Fehler ausgebügelt. Das Problem bleibt aber noch immer das gleiche :(.
 
Zuletzt bearbeitet:
Hi,

die C++ Bindings erscheinen mir seltsam. Ich hab's gerade auf Linux und NVidia's OpenCL ausprobiert und immerhin kann ich es nach etwas basteln kompilieren. Meine C++ Bindings stimmen aber nicht mit deinen überein. Die Signaturen von Platform::get() und vom Program-Konstruktor sind beispielsweise unterschiedlich. Welche OpenCL Plattform verwendest du? Treiber & SDK sind aktuell? Ggf. kannst du den Header direkt von Khronos verwenden.
Der Beispielcode kompiliert dann hier (Linux) immerhin und tut irgendwas. Die Fehlermeldung taucht hier nicht auf.
 
Mein System:
CPU: AMD Phenom II x4 944
GPU: MSI 5770 Hawk

Grafikkartentreiber habe ich schon neu installiert und ist auf dem neusten Stand (12.10). Das AMD APP SDK ist auch auf dem neusten Stand. (Die hier: http://support.amd.com/us/gpudownload/windows/Pages/radeonaiw_vista64.aspx#2 )
Die Bindings sind die ganz normalen OpenCL-Bindings in Wrapper-Klassen gepackt, damit man den Komfort von C++ hat. Sollte also eigentlich funktionieren. Die waren auch bei der AMD APP SDK dabei. Einziger Nachteil: Die sind nur für OpenCL 1.1. Welche Bindings benutzt du?
http://www.khronos.org/registry/cl/ (etwas weiter unten. cl.hpp):
cl.hpp - OpenCL 1.1 C++ Bindings Header File, implementing the C++ Bindings Specification. This header works for all versions of OpenCL, but has not yet been updated with new OpenCL 1.2 entry points.
 
Ich kann es dir gerade nicht mehr genau sagen. Hab' den Rechner leider nicht mehr vor mir. Der Header stammt mit ziemlicher Sicherheit aus einem Archlinux Paket denn irgendwelche SDKs habe ich dort nicht installiert. Das glaube ich wenigstens.
Grundsätzlich läuft auf der Kiste eine Nvidia Karte samt proprietärem Treiber.
Der Khronos-Header hat übrigens auch "meine" Version der Bindings. Such' einfach mal dort nach Platform::get. Dort schaut die Signatur anders aus als du sie verwendest. Mein Gedanke ist, dass vielleicht auf diese Art Parameter "durcheinander" kommen. Grundsätzlich würde ich auch zur C-API raten. Ich halte diese für stabiler und besser getestet. Das ist aber nur meine Vermutung und die kann ich eben nicht mit Fakten belegen. Es scheinen jedenfalls mehr Leute C als C++ in diesem Kontext zu verwenden.
 
Ansonsten scheint aber OpenCl bei mir zu funktionieren. Nur dieser Fehler kommt mir spanisch vor. Hast du vllt eine andere Idee, woran es liegen könnte?
Das Programm möchte ich eigentlich nicht von C++ in C umschreiben, da das inzwischen mit einem etwas größeren Aufwand verbunden wäre.
 
1. Wieso mapst du das denn noch ein 2. Mal? Das ist bereits gemapt nachdem du Use Host Pointer machst.

2. Ich nehme an, dass das Image3d automatisch komplett kopiert wird, sobald das Kernel gestartet wird. Denn sonst würden dir die Latenzen des PCIE sämtliche Performance rauben.

3. Da es bei Use Host Pointer das Image nur cacht, kann es sein dass es wieder aus dem Cache verdrängt wird und öfters hoch auf die Graphikkarte geladen wird.
 
So wie es ich verstanden habe, kann ich ihn mappen oder er kopiert sich einfach die Sachen, die er braucht. Meine Quellen: http://www.khronos.org/message_boards/viewtopic.php?f=28&t=3025
http://www.khronos.org/message_boards/viewtopic.php?p=6244

Habe jetzt noch etwas rumgetüftelt. Habe bis jetzt noch nicht hinbekommen einen Image3D mit 1 Byte großen Elementen mit USE_HOST_PTR oder COPY_HOST_PTR zu übertragen, sodass es funktioniert hat. (Das resultat sollte sein, dass retArray den gleichen Inhalt, wie Img3D hat.)
Hat das jemand von euch vllt hinbekommen? Wenn ja, könnt ihr euren Source mal posten?
 
Ich habe mein Programm dafür mal etwas von copy Host Pointer auf Use Host Pointer modifiziert und das funktionierte bereits so:

float Values[32]

....
VolumeTexture=cl::Image3D(CLcontext,CL_MEM_READ_ONLY|CL_MEM_USE_HOST_PTR,FormatA,XDim,YDim,ZDim,0,0,Values,0);
....

Kernel.setArg(0,VolumeTexture);

Allerdings kopiert er es erst beim Starten des Kernels; dh es gibt einen Segmentation Fault wenn man das float Array beim Starten des Kernels nicht mehr gültig.
 
Ich habe jetzt den Code etwas umgeändert. Nun bekomme ich bei retArr in den ersten 9 Feldern immerhin die Werte von 1-9, die restlichen Werte sind aber nach wie vor 0. Irgendwas stimmt noch immer nicht ganz :(.

Code:
#include "CL\cl.hpp"
#include <iostream>

using namespace std;
using namespace cl;

typedef cl_uchar BYTE;
#define STRINGIFY(...) #__VA_ARGS__

const char kernel_source[] = STRINGIFY(
	__kernel void test( __read_only image3d_t vol, sampler_t volSampler, __global int* ret){
		size_t idx = get_global_id(0);
		size_t idy = get_global_id(1);
		size_t idz = get_global_id(2);

		ret[idx + 3* (idy + 3*idz)] = read_imageui(vol, volSampler, (int4)(idx, idy, idz, 0)).w;
}
);

std::vector<CommandQueue>	m_Queues;
Program						m_Program;
Context						m_Context;
std::vector<Device>			m_Devices;
Platform					m_Platform;
Kernel						m_TestKnl;
Image3D						m_CLVol;
Sampler						m_VolSampler;

const BYTE img3D[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 };

int main(){
	m_Platform = Platform::get();
	m_Platform.getDevices(CL_DEVICE_TYPE_ALL, &m_Devices);
	m_Context = Context(m_Devices);

	m_Program = Program(m_Context, kernel_source);
	cl_int result = m_Program.build(m_Devices);
	if ( result ){
		cout<<"Error during compilation! ( "<<result<<" )\n";
		STRING_CLASS errstr;
		m_Program.getBuildInfo(m_Devices.front(), CL_PROGRAM_BUILD_LOG, &errstr);
		cout<<errstr.c_str();
		throw -1;
	} else {
		cout<<"OpenCL compiled correctly: "<<result<<endl;
	}

	m_Queues.clear();
	for(int i = 0; i < m_Devices.size(); ++i){
		m_Queues.push_back(CommandQueue(m_Context, m_Devices[i]));
	}
	cout<<"Queue-Size: "<<m_Devices.size()<<endl;

	m_TestKnl = Kernel(m_Program, "test");

	DWORD start = 0, end = 0;
	float time;
	start = GetTickCount();

	ImageFormat imgform(CL_A, CL_UNSIGNED_INT8);
	cl_int err = CL_SUCCESS;
	m_CLVol = Image3D(
		m_Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, imgform,
		3, 3, 3, 0, 0, (void*)img3D, &err);
	cout<<"\nVol-Image3D: "<<err<<endl;

	m_VolSampler = Sampler(m_Context, CL_TRUE, CL_ADDRESS_NONE, CL_FILTER_LINEAR, &err);
	cout<<"Vol-Image3D-Sampler: "<<err<<endl;


	cl_int retArr[27];
	for(int i = 0; i < 27; ++i){
		retArr[i] = 0;
	}

	err = 0;
	Buffer retBuf(m_Context, CL_MEM_WRITE_ONLY, sizeof(cl_int)*27, NULL, &err);
	if(err != 0){
		cout<<"Error PicBuffer: "<<err<<endl;
	}

	err = m_TestKnl.setArg(0, m_CLVol);
	cout<<"Arg 0: "<<err<<endl;
	err = m_TestKnl.setArg(1, sizeof(cl_sampler), &m_VolSampler);
	cout<<"Arg 1: "<<err<<endl;
	err = m_TestKnl.setArg(2, retBuf);
	cout<<"Arg 2: "<<err<<endl;

	err = m_Queues[1].enqueueNDRangeKernel(m_TestKnl, NullRange, NDRange(3, 3, 3), NullRange, NULL, NULL);
	cout<<"enqueue Kernel: "<<err<<endl;

	err = m_Queues[1].enqueueReadBuffer(retBuf, CL_TRUE, 0, sizeof(cl_int)*27, retArr);
	cout<<"enqueue retBuf: "<<err<<endl;

	end = GetTickCount();
	time = ((float)(end-start)/1000);
	cout<<"Time: "<<time<<" s\n";

	for(int i = 0; i < 27; ++i){
		cout<<retArr[i]<<" ";
	}
	cout<<endl<<endl;
	cin>>err;
	return 0;
}

EDIT:
Interessanter weise gibt dieser Kernel am Ende (nach den Zahlen 1-9) nur noch 100er, statt 0er aus. Somit müsste die Übergabe von retArr stimmen und irgend etwas mit dem Bild oder dem Sampler nicht :rolleyes:.
Code:
const char kernel_source[] = STRINGIFY(
	__kernel void test( __read_only image3d_t vol, sampler_t volSampler, __global int* ret){
		size_t idx = get_global_id(0);
		size_t idy = get_global_id(1);
		size_t idz = get_global_id(2);
		unsigned int val = read_imageui(vol, volSampler, (int4)(idx, idy, idz, 0)).x;
		if(val != 0){
			ret[idx + 3* (idy + 3*idz)] = val;
		} else{
			ret[idx + 3* (idy + 3*idz)] = 100;
		}
}
 
Zuletzt bearbeitet:
Selbstkorrektur und zum Mapping:
Punkt Nummer 1 aus dem vorletzten Post von mir war leider auch etwas falsch, da ich das nicht mehr richtig in Erinnerung hatte und das Mappen bzw Unmappen in der Funktion vertauscht hatte.

OpenCL meint zum Mapping:
The mapping/unmapping method of interaction between the host and OpenCL memory objects
allows the host to map a region from the memory object into its address space. The memory
map command may be blocking or non-blocking. Once a region from the memory object has
been mapped, the host can read or write to this region. The host unmaps the region when
accesses (reads and/or writes) to this mapped region by the host are complete.

Dadurch muss man es in seinem HostProgramm mappen, wenn man von dort etwas Reinschreiben // Lesen will. Will man im Kernel daraus zugreifen muss man es danach wieder unmappen. Nach der Erstellung mit Use Host Pointer sollte es bereits ungemapt sein. In dem ersten Code wurde es allerdings anschliessend gemapt, und danach vom Kernel aus darauf zugegriffen.

Der Code liefert bei mir kopiert und eingefügt allerdings bei NVIDIA keine Fehler mit dem Mapping. Eventuell mag die ATI Implementierung nicht, dass du bei einem Read Only Bild, was OpenCL also nicht verändern darf, den Hostspeicher nocheinmal per Read Only für den Host mapst.


Zu dem neuesten Post:
The read_imagei and read_imageui calls support a nearest filter only. The filter_mode specified in sampler must be set to CLK_FILTER_NEAREST; otherwise the values returned are undefined.
 
Zuletzt bearbeitet:
m_VolSampler = Sampler(m_Context, CL_TRUE, CL_ADDRESS_NONE, CL_FILTER_NEAREST, &err);
Mit diesem Sampler geht es auch nicht :(.
Was mich wundert ist, dass alle Elemente mit der Koordinate z=0 richtig ausgelesen werden (bei mir) und alle anderen (z=1 oder 2) falsch, also eine 0, ausgelesen werden.
 
Code:
m_VolSampler = Sampler(m_Context, CL_TRUE, CL_ADDRESS_CLAMP_TO_EDGE, CL_FILTER_NEAREST, &err);
Damit bekomm ich nur 0er :(. (imagef benutzt)
Wenn ich aber nun imageui benutze bekomme 3x hintereinander die Zahlen von 1-9. D.h. ich vermute, dass OpenCL aus dem Image3D ein Image2D macht, also alles mit z != 0 weg lässt. Aber WARUM? Mein Bild ist doch richtig deklariert, oder?
Code:
	m_CLVol = Image3D(
		m_Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, imgform,
		3, 3, 3, 0, 0, (void*)img3D, &err);
 
Das liefert dir die Werte normiert zurück; mach mal

unsigned int val = (unsigned int ) ( 255* read_imagef(vol, volSampler, (int4)(idx, idy, idz, 0)).x);

draus. Und CL_UNORM_INT8 als Formatdatentyp.

Die deklaration scheint m.E. auch zu passen, deshalb vermutete ich zuerst einen OpenCL Bug bei einer relativ "unüblichen" readimage funktion.

Ausserdem sind deine Eingabeimagedaten Signed Byte. Dein Format Unsigned Byte. Das kann dir Probleme bereiten später.
 
Zuletzt bearbeitet:
mit den readimagef von dir kommt auch drei mal die Zahlen von 1-9 raus.

Leider liegen meine Daten, die ins image3D müssen, als unsigned char (Werte von 0-255) vor und aus Platz- und Kompatibilitätsgründen muss das auch so bleiben. Am besten wäre es noch, wenn die Skalierungsfunktion funktionieren würde (CL_FILTER_LINEAR), aber ich bin erstmal froh, wenn ich die richtigen Zahlen auslesen kann...

EDIT:
Warum sind meine Eingabedaten signed Byte? Hab doch das am Anfang stehen:
Code:
typedef cl_uchar BYTE;
Das ist meine Behelfslösung, um die MFC-Bib zu ersetzen :cool_alt:
 
Zuletzt bearbeitet:
Code:
m_CLVol = Image3D(
    m_Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, imgform,
    3, 3, 3, 0, 0, (void*)img3D, &err);
Ich habe nochmal mit dem Konstruktor rumgespielt. Die Höhe und Breite scheint er richtig zu übergeben (Das Bild wird dementsprechend kleiner etc.), aber auf die Tiefe reagiert er überhaupt nicht. Es ist eigentlich egal, was ich eingebe, am Ende bekommt der Kernel ein Bild mit der Tiefe 1, selbst wenn ich Werte wie 0 oder -1 eingebe :mad:.
Ergänzung ()

Der Vollständigkeit halber:
Ich habe den Fehler gefunden! In der C++-Wrapperklassen ist ein Fehler:
Code:
class Image3D : public Image
{
public:
    Image3D(
        const Context& context,
        cl_mem_flags flags,
        ImageFormat format,
        ::size_t width,
        ::size_t height,
        ::size_t depth,
        ::size_t row_pitch = 0,
        ::size_t slice_pitch = 0,
        void* host_ptr = NULL,
        cl_int* err = NULL)
    {
        cl_int error;
#if defined(CL_VERSION_1_2)
        cl_image_desc desc;
        desc.image_type = CL_MEM_OBJECT_IMAGE2D;
Offensichtlich wird Image3D, wie in der letzten Zeile zu sehen, als Image2D implementiert! Tja jetzt muss ich mein Programm entweder komplett mit den normalen C-Bindings schreiben oder ich verbessere es und hoffe das kein anderer Fehler kommt.
Auf der Khronos ist eine verbesserte Version schon länger verfügbar. Nur hat AMD anscheinend diese noch nicht eingepflegt :rolleyes:.
 
Zuletzt bearbeitet:
Hallo Leute,

ich habe ein neues Problem :mad:. Ich benutze den normalen Cl.h-Header, somit sollte das Problem diesmal nicht daran liegen.
Ich will ganz einfach Daten in ein 2D-Bild schreiben. Deswegen habe ich den vorherigen Code erweitert. Es wird aber nichts in das Bild geschieben. OpenCl gibt mir immer 0 zurück, es sollte also keine Fehler geben (und alles funktionieren). Es passiert aber auch nichts :(.
Die Queues, Devices etc wurden von oben übernommen, da sollte also nicht der Fehler liegen, da das vorherige Teil des Codes nach wie vor funktioniert.
Ich will aus einem 2D Array mit float4, dass die Werte 0-63 (also 4x4 großes Array = 16 float4; 16 float4 = 64 floats ) besitzt in ein ebenso großes image2D übertragen. Es wird aber nichts übertragen, da die ursprünglichen Werte (-1), nach Ausführen des Kernels, immer noch drin stehen. Ich verwende für das zu beschreibende image2D das mem_flag USE_HOST_PTR, es wäre gut, wenn es damit funktionieren würde, aber ein anderes flag wäre auch kein Beinbruch.

Hier der Kernelcode:
Code:
__kernel void img2Dtest( __write_only image2d_t pic, __global float4* arr){
	size_t idx = get_global_id(0);
	size_t idy = get_global_id(1);
	int2 pos = (int2)(idx, idy);
	float4 val = arr[idx * 4*idy];
	write_imagef(pic, pos, val);
}

Hier der hinzugefügte C(++)-Code:
Code:
	cl_float4* arrayData = get2DArray();
	start = GetTickCount();

	cl_float4 retArr2[size_2D*size_2D];

	for(int i = 0; i < size_2D*size_2D; ++i){
		for(int j = 0; j < 4; ++j){
			retArr2[i].s[j] = -1.0f;
		}
	}

	imgForm.image_channel_order = CL_RGBA;
	imgForm.image_channel_data_type = CL_FLOAT;

	imgDesc.image_type = CL_MEM_OBJECT_IMAGE2D;
	imgDesc.image_width = size_2D;
	imgDesc.image_height = size_2D;
	imgDesc.image_depth = 1;
	imgDesc.image_array_size = 1;
	imgDesc.image_row_pitch = imgDesc.image_slice_pitch = 0;
	imgDesc.num_mip_levels = imgDesc.num_samples = 0;
	imgDesc.buffer = NULL;

	m_Img2D = clCreateImage( m_Context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, &imgForm, &imgDesc, retArr2, &err);
	cout<<"\nImage2D: "<<err<<endl;

	err = 0;
	cl_mem arrBuf = clCreateBuffer(m_Context, CL_MEM_READ_ONLY, sizeof(cl_float4)*size_2D*size_2D, NULL, &err);
	cout<<"arrBuf: "<<err<<endl;

	err = clSetKernelArg(m_Img3DTestKnl, 0, sizeof(cl_mem), &m_Img2D);
	cout<<"Arg 0: "<<err<<endl;
	err = clSetKernelArg(m_Img3DTestKnl, 1, sizeof(cl_mem), &arrBuf);
	cout<<"Arg 1: "<<err<<endl;
	
	err = clEnqueueWriteBuffer(m_Queue, arrBuf, CL_TRUE, 0, sizeof(cl_float4)*size_2D*size_2D, arrayData, NULL, NULL, NULL);
	cout<<"enqueue arrBuf: "<<err<<endl;

	size_t workDims2[] = { size_2D, size_2D };
	err = clEnqueueNDRangeKernel(m_Queue, m_Img3DTestKnl, 2, NULL, workDims2, NULL, NULL, NULL, NULL);
	cout<<"enqueue Kernel: "<<err<<endl;

	end = GetTickCount();
	time = ((float)(end-start)/1000);
	cout<<"Time: "<<time<<" s\n";

	for(int i = 0; i < size_2D*size_2D; ++i){
		for(int j = 0; j < 4; ++j){
			cout<<retArr2[i].s[j]<<"\t";
		}
		cout<<endl;
	}
	cout<<endl<<endl;
	cin>>err;
	delete[] arrayData;
	return 0;

und hier der gesamte Code, falls es jemand bei sich ausprobieren möchte:
Code:
#include "CL\cl.h"
#include <Windows.h>
#include <iostream>

using namespace std;

typedef cl_uchar BYTE;
#define STRINGIFY(...) #__VA_ARGS__
#define size_3D 3
#define size_2D 4

cl_float4* get2DArray();

const char* kernel_source = STRINGIFY(
	__kernel void img3Dtest( __read_only image3d_t vol, sampler_t volSampler, __global int* ret){
		size_t idx = get_global_id(0);
		size_t idy = get_global_id(1);
		size_t idz = get_global_id(2);
		uchar val = read_imageui(vol, volSampler, (float4)((float)idx, (float)idy, (float)idz, 0)).x;
		ret[idx + 3* (idy + 3*idz)] = val;
}

__kernel void img2Dtest( __write_only image2d_t pic, __global float4* arr){
	size_t idx = get_global_id(0);
	size_t idy = get_global_id(1);
	int2 pos = (int2)(idx, idy);
	float4 val = arr[idx * 4*idy];
	write_imagef(pic, pos, val);
}
);

const BYTE img3D[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 };

int main(){
	cl_command_queue	m_Queue = 0;
	cl_program			m_Program = 0;
	cl_context			m_Context = 0;
	cl_device_id		m_Device = 0;
	cl_platform_id		m_Platform = 0;
	cl_kernel			m_Img3DTestKnl = 0;
	cl_kernel			m_Img2DTestKnl = 0;
	cl_mem				m_CLVol = 0;
	cl_mem				m_Img2D = 0;
	cl_sampler			m_VolSampler = 0;
	cl_int err = CL_SUCCESS;

	err = clGetPlatformIDs(1, &m_Platform, NULL);
	cout<<"Platform: "<<err<<endl;

	err = clGetDeviceIDs(m_Platform, CL_DEVICE_TYPE_GPU, 1, &m_Device, NULL);
	if(err == CL_DEVICE_NOT_FOUND){
		cout<<"No CL_DEVICE_TYPE_GPU found! Searching other device.\n";
		err = clGetDeviceIDs(m_Platform, CL_DEVICE_TYPE_ALL, 1, &m_Device, NULL);
	}
	cout<<"GetDevices: "<<err<<endl;
	m_Context = clCreateContext(0, 1, &m_Device, NULL, NULL, &err);

	m_Program = clCreateProgramWithSource(m_Context, 1, &kernel_source, 0, &err);
	cout<<"CreateProgramWithSource: "<<err<<endl;
	err = clBuildProgram(m_Program, 1, &m_Device, NULL, NULL, NULL);
	if ( err ){
		cout<<"Error during compilation! ( "<<err<<" )\n";
		char errstr[256];
		err = clGetProgramBuildInfo(m_Program, m_Device, CL_PROGRAM_BUILD_LOG, 256, errstr, NULL);
		cout<<"ERROR MESSAGE: \n"<<errstr<<endl<<"Error Code: "<<err<<endl;
		throw -1;
	} else {
		cout<<"OpenCL compiled correctly: "<<err<<endl;
	}

	m_Queue = clCreateCommandQueue(m_Context, m_Device, 0, &err); 
	cout<<"Create Queue: "<<err<<endl;
	
	m_Img3DTestKnl = clCreateKernel(m_Program, "img3Dtest", &err);
	cout<<"Create Kernel3D: "<<err<<endl;
	m_Img2DTestKnl = clCreateKernel(m_Program, "img2Dtest", &err);
	cout<<"Create Kernel2D: "<<err<<endl;

	///////////////////////////////////////////////////////////////////////////TEST3D

	DWORD start = 0, end = 0;
	float time;
	start = GetTickCount();

	cl_image_format imgForm;
	imgForm.image_channel_order = CL_R;
	imgForm.image_channel_data_type = CL_UNSIGNED_INT8;

	cl_image_desc imgDesc;
	imgDesc.image_type = CL_MEM_OBJECT_IMAGE3D;
	imgDesc.image_width = 3;
	imgDesc.image_height = 3;
	imgDesc.image_depth = 3;
	imgDesc.image_array_size = 1;
	imgDesc.image_row_pitch = imgDesc.image_slice_pitch = 0;
	imgDesc.num_mip_levels = imgDesc.num_samples = 0;
	imgDesc.buffer = NULL;

	m_CLVol = clCreateImage( m_Context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, &imgForm, &imgDesc, (void*)img3D, &err);
	cout<<"\nVol-Image3D: "<<err<<endl;

	m_VolSampler = clCreateSampler(m_Context, CL_FALSE, CL_ADDRESS_NONE, CL_FILTER_NEAREST, &err);
	cout<<"Vol-Image3D-Sampler: "<<err<<endl;


	cl_int retArr[27];
	for(int i = 0; i < 27; ++i){
		retArr[i] = 0;
	}

	err = 0;
	cl_mem retBuf = clCreateBuffer(m_Context, CL_MEM_WRITE_ONLY, sizeof(cl_int)*27, NULL, &err);
	cout<<"retBuffer: "<<err<<endl;

	err = clSetKernelArg(m_Img3DTestKnl, 0, sizeof(cl_mem), &m_CLVol);
	cout<<"Arg 0: "<<err<<endl;
	err = clSetKernelArg(m_Img3DTestKnl, 1, sizeof(cl_sampler), &m_VolSampler);
	cout<<"Arg 1: "<<err<<endl;
	err = clSetKernelArg(m_Img3DTestKnl, 2, sizeof(cl_mem), &retBuf);
	cout<<"Arg 2: "<<err<<endl;

	size_t workDims[] = { 3, 3, 3 };
	err = clEnqueueNDRangeKernel(m_Queue, m_Img3DTestKnl, 3, NULL, workDims, NULL, NULL, NULL, NULL);
	cout<<"enqueue Kernel: "<<err<<endl;

	err = clEnqueueReadBuffer(m_Queue, retBuf, CL_TRUE, 0, sizeof(cl_int)*27, retArr, NULL, NULL, NULL);
	cout<<"enqueue retBuf: "<<err<<endl;

	end = GetTickCount();
	time = ((float)(end-start)/1000);
	cout<<"Time: "<<time<<" s\n";

	for(int i = 0; i < 27; ++i){
		cout<<retArr[i]<<" ";
	}
	cout<<endl<<endl;

	///////////////////////////////////////////////////////TEST2D
	cl_float4* arrayData = get2DArray();
	start = GetTickCount();

	cl_float4 retArr2[size_2D*size_2D];

	for(int i = 0; i < size_2D*size_2D; ++i){
		for(int j = 0; j < 4; ++j){
			retArr2[i].s[j] = -1.0f;
		}
	}

	imgForm.image_channel_order = CL_RGBA;
	imgForm.image_channel_data_type = CL_FLOAT;

	imgDesc.image_type = CL_MEM_OBJECT_IMAGE2D;
	imgDesc.image_width = size_2D;
	imgDesc.image_height = size_2D;
	imgDesc.image_depth = 1;
	imgDesc.image_array_size = 1;
	imgDesc.image_row_pitch = imgDesc.image_slice_pitch = 0;
	imgDesc.num_mip_levels = imgDesc.num_samples = 0;
	imgDesc.buffer = NULL;

	m_Img2D = clCreateImage( m_Context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, &imgForm, &imgDesc, retArr2, &err);
	cout<<"\nImage2D: "<<err<<endl;

	err = 0;
	cl_mem arrBuf = clCreateBuffer(m_Context, CL_MEM_READ_ONLY, sizeof(cl_float4)*size_2D*size_2D, NULL, &err);
	cout<<"arrBuf: "<<err<<endl;

	err = clSetKernelArg(m_Img3DTestKnl, 0, sizeof(cl_mem), &m_Img2D);
	cout<<"Arg 0: "<<err<<endl;
	err = clSetKernelArg(m_Img3DTestKnl, 1, sizeof(cl_mem), &arrBuf);
	cout<<"Arg 1: "<<err<<endl;
	
	err = clEnqueueWriteBuffer(m_Queue, arrBuf, CL_TRUE, 0, sizeof(cl_float4)*size_2D*size_2D, arrayData, NULL, NULL, NULL);
	cout<<"enqueue arrBuf: "<<err<<endl;

	size_t workDims2[] = { size_2D, size_2D };
	err = clEnqueueNDRangeKernel(m_Queue, m_Img3DTestKnl, 2, NULL, workDims2, NULL, NULL, NULL, NULL);
	cout<<"enqueue Kernel: "<<err<<endl;

	end = GetTickCount();
	time = ((float)(end-start)/1000);
	cout<<"Time: "<<time<<" s\n";

	for(int i = 0; i < size_2D*size_2D; ++i){
		for(int j = 0; j < 4; ++j){
			cout<<retArr2[i].s[j]<<"\t";
		}
		cout<<endl;
	}
	cout<<endl<<endl;
	cin>>err;
	delete[] arrayData;
	return 0;
}

cl_float4* get2DArray(){
	cl_float4* arr = new cl_float4[size_2D*size_2D];
	for(int i = 0; i < size_2D*size_2D; ++ i){
		for(int j = 0; j < 4; ++j){
			arr[i].s[j] = j + 4*i;
			cout<<arr[i].s[j]<<"\t";
		}
		cout<<endl;
	}
	return arr;
}

Danke für eure kompetente Hilfe :).
 
Danke für eure kompetente Hilfe .
Ich hab doch auch keinerlei Plan und Ahnung :)

Die meisten OpenCL Calls blockieren nicht. Deshalb würde ich es so spontan mit einem clfinish probieren nach dem Enqueuen des Kernels.

Leider bin ich mir immer noch etwas unsicher, wann genau die Änderungen an dem Bild bei USE_HOST_PTR im Hauptspeicher sichtbar sind. Denn ich habe das immer mit clenqueuereadimage gemacht. Leider konnte ich darauf auch so spontan keinen Hinweis in der OpenCL Dokumentation finden.

Aber ich hoffe mal dass das CLFinish dafür reicht, dass das Bild zurückübertragen wird.
 
Code:
err = clFinish(m_Queue);
cout<<"finish Queue: "<<err<<endl;
mit diesem Code nach clEnqueueNDRangeKernel passiert noch immer nichts, also scheint der Kernel schon fertig zu sein :(.

EDIT:
Habe jetzt etwas rumgespielt.
Habe nach dem enqueueKernel ein readImage-Befehl eingefügt:
Code:
	size_t origin[] = { 0, 0, 0 };
	size_t region[] = { size_2D, size_2D, 1 };
	err = clEnqueueReadImage(m_Queue, m_Img2D, CL_TRUE, origin, region, 0, 0, retArr2, 0, NULL, NULL);
	cout<<"read Image: "<<err<<endl;
und die Erstellung des Bildes etwas geändert:
Code:
	m_Img2D = clCreateImage( m_Context, CL_MEM_WRITE_ONLY, &imgForm, &imgDesc, NULL, &err);
	cout<<"\nImage2D: "<<err<<endl;
Nun kommt bei mir bei allen Werten 0 raus. Das Array ist aber mit -1 initialisiert. D.h. irgendwelche Daten müssen jetzt übertragen werden, aber nicht die richtigen :(.
 
Zuletzt bearbeitet:
float4 val = arr[idx * 4*idy];

Müsste das nicht
float4 val = arr[ idx + 4*idy];
sein ?


cl_mem arrBuf = clCreateBuffer(m_Context, CL_MEM_READ_ONLY, sizeof(cl_float4)*size_2D*size_2D, NULL, &err);
cout<<"arrBuf: "<<err<<endl;

err = clSetKernelArg(m_Img3DTestKnl, 0, sizeof(cl_mem), &m_Img2D);
cout<<"Arg 0: "<<err<<endl;
err = clSetKernelArg(m_Img3DTestKnl, 1, sizeof(cl_mem), &arrBuf);
cout<<"Arg 1: "<<err<<endl;

Du initialisiert deinen Buffer nicht auf den du lesend zugreifst. Wenn das immer noch nicht geht suche ich mal weiter. Kann das leider hier nicht testen, da ich kein OpenCL habe.

P.S. Was soll das Projekt überhaupt werden, wenn es mal fertig ist?
 
Zuletzt bearbeitet:

Ähnliche Themen

F
2
Antworten
21
Aufrufe
2.888
F
F
Antworten
6
Aufrufe
1.321
Zurück
Oben